Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
# v5.9.0
# New
- Documented `public/{slugOrId}` endpoints
- You can now access public bar data via `public/{slugOrId}` endpoints
- Added GET `public/{slugOrId}/menu` endpoint
- Added GET `public/{slugOrId}/cocktails` endpoint
- Added GET `public/{slugOrId}/cocktails/{cocktailId}` endpoint
- Added `is_public` property to `Bar` schema
- If set to `true`, bar will expose public endpoints `/public/{barId}/*`
- Added `is_menu_enabled` to public `Bar` schema

## Fixes
- Fixed search driver indexing calls when not using search driver
- Default ingredient unit bar setting is not correctly applied when importing data via datapack
- Fixed unique constraint violation when adding ingredients into bar shelf

# v5.8.1
## Fixes
- Fixed missing API ability on `import/cocktail` endpoint

# v5.8.0
## New
- Added `bars/{id}/sync-datapack` endpoint
Expand Down
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,12 @@ This repository only contains the API server, if you are looking for easy to use
- Data export support in various formats
- Support for multiple ingredient prices
- Automatic cocktail price calculation based on ingredients
- SSO Support
- Single sign-on (SSO) support
- Recipe recommendations based on your favorites and tags

## Documentation

[Documentation is available here.](https://docs.barassistant.app/)

## Container images

Expand All @@ -63,13 +68,13 @@ We recommend that you always use the latest major release, as it will always be

## Managed instance

Bar Assistant will always be open-source and MIT-licensed, but if you want to support the project or don't want to self-host, you can try officialy managed instance. Visit [barassistant.app](https://barassistant.app/) for more information about our cloud offering.
Bar Assistant will always be open-source and MIT-licensed, but if you want to support the project or don't want to self-host, you can try our official managed instance. Visit [barassistant.app](https://barassistant.app/) for more information about our cloud offering.

![Cloud offering screenshot](/resources/art/art1.png)

## Documentation
## 3rd Party Integrations

[Documentation is available here.](https://bar-assistant.github.io/docs/)
There's an [unofficial Raycast extension](https://www.raycast.com/stupifier/barassistant) maintained by a [community member](https://github.com/zhdenny).

## Contributing

Expand Down
6 changes: 3 additions & 3 deletions app/External/Export/ToDataPack.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public function process(int $barId, ?string $filename = null, ForceUnitConvertEn
}

$this->dumpCocktails($barId, $zip, $toUnits);
$this->dumpIngredients($barId, $zip);
$this->dumpIngredients($barId, $zip, $toUnits);
$this->dumpBaseData($barId, $zip);
$this->dumpCalculators($barId, $zip);

Expand Down Expand Up @@ -111,13 +111,13 @@ private function dumpCocktails(int $barId, ZipArchive &$zip, ?Units $toUnits = n
}
}

private function dumpIngredients(int $barId, ZipArchive &$zip): void
private function dumpIngredients(int $barId, ZipArchive &$zip, ?Units $toUnits = null): void
{
$ingredients = Ingredient::with('images.imageable', 'calculator', 'prices.priceCategory', 'ingredientParts.ingredient.parentIngredient', 'ancestors', 'parentIngredient')->where('bar_id', $barId)->get();

/** @var Ingredient $ingredient */
foreach ($ingredients as $ingredient) {
$data = IngredientExternal::fromModel($ingredient, true);
$data = IngredientExternal::fromModel($ingredient, true, $toUnits);

/** @var \Kami\Cocktail\Models\Image $img */
foreach ($ingredient->images as $img) {
Expand Down
18 changes: 13 additions & 5 deletions app/External/Import/FromDataPack.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,12 @@ public function process(Filesystem $dataDisk, Bar $bar, User $user, ?BarOptionsE

$bar->setStatus(BarStatusEnum::Active)->save();

/** @phpstan-ignore-next-line */
Ingredient::where('bar_id', $bar->id)->searchable();
/** @phpstan-ignore-next-line */
Cocktail::where('bar_id', $bar->id)->searchable();
if (!empty(config('scout.driver'))) {
/** @phpstan-ignore-next-line */
Ingredient::where('bar_id', $bar->id)->searchable();
/** @phpstan-ignore-next-line */
Cocktail::where('bar_id', $bar->id)->searchable();
}

$timerEnd = microtime(true);

Expand Down Expand Up @@ -192,6 +194,12 @@ private function importIngredients(Filesystem $dataDisk, Bar $bar, User $user):
continue;
}

$ingredientUnit = $externalIngredient->units?->value;
$barSettings = $bar->settings ?? [];
if ($externalIngredient->units?->isConvertable() && array_key_exists('default_units', $barSettings)) {
$ingredientUnit = $barSettings['default_units'];
}

$slug = $externalIngredient->id . '-' . $bar->id;
$ingredientsToInsert[] = [
'bar_id' => $bar->id,
Expand All @@ -208,7 +216,7 @@ private function importIngredients(Filesystem $dataDisk, Bar $bar, User $user):
'sugar_g_per_ml' => $externalIngredient->sugarContent,
'acidity' => $externalIngredient->acidity,
'distillery' => $externalIngredient->distillery,
'units' => $externalIngredient->units,
'units' => $ingredientUnit,
];

if ($externalIngredient->parentId) {
Expand Down
19 changes: 13 additions & 6 deletions app/External/Model/Ingredient.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
namespace Kami\Cocktail\External\Model;

use Kami\Cocktail\External\SupportsCSV;
use Kami\RecipeUtils\UnitConverter\Units;
use Kami\Cocktail\External\SupportsDraft2;
use Kami\Cocktail\Models\ComplexIngredient;
use Kami\Cocktail\External\SupportsDataPack;
use Kami\Cocktail\Models\Image as ImageModel;
use Kami\Cocktail\Models\ValueObjects\UnitValueObject;
use Kami\Cocktail\Models\Ingredient as IngredientModel;
use Kami\Cocktail\Models\IngredientPrice as IngredientPriceModel;

Expand Down Expand Up @@ -37,11 +39,11 @@ private function __construct(
public ?float $sugarContent = null,
public ?float $acidity = null,
public ?string $distillery = null,
public ?string $units = null,
public ?UnitValueObject $units = null,
) {
}

public static function fromModel(IngredientModel $model, bool $useFileURI = false): self
public static function fromModel(IngredientModel $model, bool $useFileURI = false, ?Units $toUnits = null): self
{
$images = $model->images->map(function (ImageModel $image) use ($useFileURI) {
return Image::fromModel($image, $useFileURI);
Expand All @@ -55,6 +57,11 @@ public static function fromModel(IngredientModel $model, bool $useFileURI = fals
return IngredientPrice::fromModel($price);
})->toArray();

$defaultIngredientUnits = $model->getDefaultUnits();
if ($model->getDefaultUnits()?->isConvertable() && $toUnits) {
$defaultIngredientUnits = new UnitValueObject($toUnits->value);
}

return new self(
id: $model->getExternalId(),
name: $model->name,
Expand All @@ -73,7 +80,7 @@ public static function fromModel(IngredientModel $model, bool $useFileURI = fals
sugarContent: $model->sugar_g_per_ml,
acidity: $model->acidity,
distillery: $model->distillery,
units: $model->getDefaultUnits()?->value,
units: $defaultIngredientUnits,
);
}

Expand Down Expand Up @@ -112,7 +119,7 @@ public static function fromDataPackArray(array $sourceArray): self
sugarContent: $sourceArray['sugar_g_per_ml'] ?? null,
acidity: $sourceArray['acidity'] ?? null,
distillery: $sourceArray['distillery'] ?? null,
units: $sourceArray['units'] ?? null,
units: ($sourceArray['units'] ?? null) ? new UnitValueObject($sourceArray['units']) : null,
);
}

Expand All @@ -136,7 +143,7 @@ public function toDataPackArray(): array
'sugar_g_per_ml' => $this->sugarContent,
'acidity' => $this->acidity,
'distillery' => $this->distillery,
'units' => $this->units,
'units' => $this->units?->value,
];
}

Expand All @@ -161,7 +168,7 @@ public static function fromCSV(array $sourceArray): self
sugarContent: blank($sourceArray['sugar_g_per_ml']) ? null : $sourceArray['sugar_g_per_ml'],
acidity: blank($sourceArray['acidity']) ? null : $sourceArray['acidity'],
distillery: blank($sourceArray['distillery']) ? null : $sourceArray['distillery'],
units: blank($sourceArray['units']) ? null : $sourceArray['units'],
units: blank($sourceArray['units']) ? null : new UnitValueObject($sourceArray['units']),
);
}

Expand Down
7 changes: 4 additions & 3 deletions app/Http/Controllers/BarController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Kami\Cocktail\Http\Controllers;

use Throwable;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Kami\Cocktail\Models\Bar;
Expand Down Expand Up @@ -87,7 +88,7 @@ public function show(Request $request, int $id): JsonResource
public function store(BarRequest $request): JsonResponse
{
if ($request->user()->cannot('create', Bar::class)) {
abort(403, 'You can not create anymore bars');
abort(403, 'You can not create any more bars.');
}

Cache::forget('metrics_bass_total_bars');
Expand All @@ -106,7 +107,7 @@ public function store(BarRequest $request): JsonResponse
try {
$imageModels = Image::findOrFail($barRequest->images);
$bar->attachImages($imageModels);
} catch (\Throwable $e) {
} catch (Throwable $e) {
abort(500, $e->getMessage());
}
}
Expand Down Expand Up @@ -167,7 +168,7 @@ public function update(int $id, BarRequest $request): JsonResource
try {
$imageModels = Image::findOrFail($barRequest->images);
$bar->attachImages($imageModels);
} catch (\Throwable $e) {
} catch (Throwable $e) {
abort(500, $e->getMessage());
}
}
Expand Down
4 changes: 3 additions & 1 deletion app/Http/Controllers/GlassController.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,9 @@ public function update(int $id, GlassRequest $request): JsonResource
}
}

$glass->cocktails->each(fn ($cocktail) => $cocktail->searchable());
if (!empty(config('scout.driver'))) {
$glass->cocktails->each(fn ($cocktail) => $cocktail->searchable());
}

return new GlassResource($glass);
}
Expand Down
18 changes: 9 additions & 9 deletions app/Http/Controllers/MenuController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
use Kami\Cocktail\Rules\ResourceBelongsToBar;
use Kami\Cocktail\Http\Resources\MenuResource;
use Illuminate\Http\Resources\Json\JsonResource;
use Kami\Cocktail\Models\Enums\MenuItemTypeEnum;
use Kami\Cocktail\OpenAPI\Schemas\MenuItemRequest;
use Kami\Cocktail\Http\Resources\MenuPublicResource;
use Kami\Cocktail\OpenAPI\Schemas\MenuRequest as SchemasMenuRequest;

class MenuController extends Controller
{
Expand Down Expand Up @@ -98,27 +99,26 @@ public function update(MenuRequest $request): MenuResource
abort(403);
}

/** @var array<mixed> */
$items = $request->input('items', []);
$schemaRequest = SchemasMenuRequest::fromIlluminateRequest($request);

$ingredients = collect($items)->where('type', MenuItemTypeEnum::Ingredient->value)->values()->toArray();
$cocktails = collect($items)->where('type', MenuItemTypeEnum::Cocktail->value)->values()->toArray();
$ingredients = $schemaRequest->getIngredients();
$cocktails = $schemaRequest->getCocktails();

Validator::make($ingredients, [
Validator::make(collect($ingredients)->map(fn (MenuItemRequest $mi) => ['id' => $mi->id])->toArray(), [
'*.id' => [new ResourceBelongsToBar(bar()->id, 'ingredients')],
])->validate();

Validator::make($cocktails, [
Validator::make(collect($cocktails)->map(fn (MenuItemRequest $mi) => ['id' => $mi->id])->toArray(), [
'*.id' => [new ResourceBelongsToBar(bar()->id, 'cocktails')],
])->validate();

$menu = Menu::firstOrCreate(['bar_id' => bar()->id]);
$menu->is_enabled = $request->boolean('is_enabled');
$menu->is_enabled = $schemaRequest->isEnabled;
if (!$menu->created_at) {
$menu->created_at = now();
}
$menu->updated_at = now();
$menu->syncItems($items);
$menu->syncItems($schemaRequest->items);
$menu->save();

return new MenuResource($menu);
Expand Down
10 changes: 5 additions & 5 deletions app/Http/Controllers/Public/BarController.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,18 @@

class BarController extends Controller
{
#[OAT\Get(path: '/public/{barId}', tags: ['Public'], operationId: 'showPublicBar', description: 'Show public information about a single bar. To access this endpoint the bar must be marked as public.', summary: 'Show bar', parameters: [
new OAT\Parameter(name: 'barId', in: 'path', required: true, description: 'Database id of bar', schema: new OAT\Schema(type: 'number')),
#[OAT\Get(path: '/public/{slugOrId}', tags: ['Public'], operationId: 'showPublicBar', description: 'Show public information about a single bar. To access this endpoint the bar must be marked as public.', summary: 'Show bar', parameters: [
new OAT\Parameter(name: 'slugOrId', in: 'path', required: true, description: 'Database id of bar', schema: new OAT\Schema(type: 'string')),
new BAO\Parameters\PageParameter(),
], security: [])]
#[BAO\SuccessfulResponse(content: [
new BAO\WrapObjectWithData(BarResource::class),
])]
#[BAO\NotFoundResponse]
public function show(int $barId): BarResource
public function show(string $slugOrId): BarResource
{
$bar = Bar::findOrFail($barId);
if (!$bar->is_public) {
$bar = Bar::where('slug', $slugOrId)->orWhere('id', $slugOrId)->firstOrFail();
if (!$bar->isPublic()) {
abort(404);
}

Expand Down
Loading