Skip to content

Commit

Permalink
feat: Align vehicle harpoint structure to item ports
Browse files Browse the repository at this point in the history
Implements #131
  • Loading branch information
octfx committed Sep 14, 2024
1 parent a37f990 commit 88eeba6
Show file tree
Hide file tree
Showing 7 changed files with 875 additions and 3 deletions.
3 changes: 0 additions & 3 deletions app/Http/Controllers/Api/V2/SC/Vehicle/VehicleController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use OpenApi\Attributes as OA;
use Spatie\QueryBuilder\AllowedFilter;
Expand Down Expand Up @@ -114,8 +113,6 @@ enum: [
)]
public function show(Request $request): AbstractBaseResource
{
DB::enableQueryLog();

['vehicle' => $identifier] = Validator::validate(
[
'vehicle' => $request->vehicle,
Expand Down
187 changes: 187 additions & 0 deletions app/Http/Controllers/Api/V3/SC/Vehicle/VehicleController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api\V3\SC\Vehicle;

use App\Http\Resources\AbstractBaseResource;
use App\Http\Resources\SC\Vehicle\VehicleResourceV3;
use App\Http\Resources\StarCitizen\Vehicle\VehicleResource;
use App\Models\SC\Vehicle\Vehicle as UnpackedVehicle;
use App\Models\StarCitizen\Vehicle\Vehicle\Vehicle;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Support\Facades\Validator;
use OpenApi\Attributes as OA;
use Spatie\QueryBuilder\QueryBuilder;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class VehicleController extends \App\Http\Controllers\Api\V2\SC\Vehicle\VehicleController
{
#[OA\Get(
path: '/api/v3/vehicles',
tags: ['Vehicles', 'RSI-Website', 'In-Game'],
parameters: [
new OA\Parameter(ref: '#/components/parameters/page'),
new OA\Parameter(ref: '#/components/parameters/limit'),
new OA\Parameter(ref: '#/components/parameters/locale'),
new OA\Parameter(name: 'filter[manufacturer]', in: 'query', schema: new OA\Schema(type: 'string')),
new OA\Parameter(name: 'filter[chassis_id]', in: 'query', schema: new OA\Schema(type: 'integer')),
],
responses: [
new OA\Response(
response: 200,
description: 'List of Vehicles',
content: new OA\JsonContent(
type: 'array',
items: new OA\Items(ref: '#/components/schemas/vehicle_link_v2')
)
),
]
)]
public function index(Request $request): AnonymousResourceCollection
{
return parent::index($request);
}

#[OA\Get(
path: '/api/v3/vehicles/{name}',
tags: ['Vehicles', 'RSI-Website', 'In-Game'],
parameters: [
new OA\Parameter(ref: '#/components/parameters/locale'),
new OA\Parameter(
name: 'filter[ports]',
description: 'Filter port types, prefix with "!" to remove these ports.',
in: 'query', schema: new OA\Schema(type: 'string')
),
new OA\Parameter(
name: 'include',
in: 'query',
schema: new OA\Schema(
description: 'Available Vehicle includes',
type: 'array',
items: new OA\Items(
type: 'string',
enum: [
'components',
'ports',
'shops',
]
),
),
explode: false,
allowReserved: true
),
new OA\Parameter(
name: 'name',
in: 'path',
required: true,
schema: new OA\Schema(
description: '(Partial) Vehicle name',
type: 'string',
),
),
],
responses: [
new OA\Response(
response: 200,
description: 'A singular vehicle',
content: new OA\JsonContent(
oneOf: [
new OA\Schema(ref: '#/components/schemas/sc_vehicle_v3'),
new OA\Schema(ref: '#/components/schemas/vehicle_v2'),
],
)
),
]
)]
public function show(Request $request): AbstractBaseResource
{
['vehicle' => $identifier] = Validator::validate(
[
'vehicle' => $request->vehicle,
],
[
'vehicle' => 'required|string|min:1|max:255',
]
);

$identifier = $this->cleanQueryName($identifier);
$underscored = str_replace(' ', '_', $identifier);

try {
$vehicleModel = QueryBuilder::for(UnpackedVehicle::class)
->where('name', $identifier)
->orWhere('class_name', $underscored)
->orWhere('class_name', 'LIKE', "%_$underscored")
->orWhere('class_name', $identifier)
->orWhere('item_uuid', $identifier)
->with([
'armor',
'flightController',
'quantumDrives',
'shields',
'thrusters',
'partsWithoutParent',
])
->first();

if ($vehicleModel === null) {
$vehicleModel = QueryBuilder::for(Vehicle::class, $request)
->where('name', $identifier)
->orWhere('slug', $identifier)
->orWhereRelation('sc', 'item_uuid', $identifier)
->firstOrFail();

return new VehicleResource($vehicleModel);
}
} catch (ModelNotFoundException $e) {
throw new NotFoundHttpException('No Vehicle with specified name found.'.$request->vehicle);
}

return new VehicleResourceV3($vehicleModel);
}

#[OA\Post(
path: '/api/v3/vehicles/search',
requestBody: new OA\RequestBody(
description: 'Vehicle (partial) name or slug',
required: true,
content: [
new OA\MediaType(
mediaType: 'application/json',
schema: new OA\Schema(
type: 'object',
),
example: '{"query": "Merchant"}',
),
]
),
tags: ['Vehicles', 'RSI-Website', 'In-Game', 'Search'],
parameters: [
new OA\Parameter(ref: '#/components/parameters/page'),
new OA\Parameter(ref: '#/components/parameters/limit'),
new OA\Parameter(ref: '#/components/parameters/locale'),
new OA\Parameter(name: 'filter[manufacturer]', in: 'query', schema: new OA\Schema(type: 'string')),
],
responses: [
new OA\Response(
response: 200,
description: 'List of vehicles matching the query',
content: new OA\JsonContent(
type: 'array',
items: new OA\Items(ref: '#/components/schemas/vehicle_link_v2')
)
),
new OA\Response(
response: 404,
description: 'No vehicle(s) found.',
),
],
)]
public function search(Request $request): AnonymousResourceCollection
{
return parent::search($request);
}
}
79 changes: 79 additions & 0 deletions app/Http/Resources/SC/Vehicle/HardpointItemResourceV3.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

declare(strict_types=1);

namespace App\Http\Resources\SC\Vehicle;

use App\Http\Resources\SC\Item\ItemContainerResource;
use App\Http\Resources\SC\Item\ItemPortResource;
use App\Http\Resources\SC\Item\ItemResource;
use App\Http\Resources\SC\Manufacturer\ManufacturerLinkResource;
use Illuminate\Http\Request;
use OpenApi\Attributes as OA;

#[OA\Schema(
schema: 'hardpoint_item_v3',
title: 'Hardpoint Item',
description: 'Trimmed down version of schema item_v2, used on hardpoints.',
allOf: [
new OA\Schema(
properties: [
new OA\Property(property: 'uuid', type: 'string'),
new OA\Property(property: 'name', type: 'string'),
new OA\Property(property: 'class_name', type: 'string'),
new OA\Property(property: 'link', type: 'string'),
new OA\Property(property: 'size', type: 'integer', nullable: true),
new OA\Property(property: 'mass', type: 'double', nullable: true),
new OA\Property(property: 'grade', type: 'string', nullable: true),
new OA\Property(property: 'class', type: 'string', nullable: true),
new OA\Property(property: 'manufacturer', ref: '#/components/schemas/manufacturer_link_v2'),
new OA\Property(property: 'type', type: 'string', nullable: true),
new OA\Property(property: 'sub_type', type: 'string', nullable: true),
new OA\Property(property: 'inventory', ref: '#/components/schemas/item_container_v2', nullable: true),
new OA\Property(property: 'ports', ref: '#/components/schemas/item_port_data_v2', nullable: true),
],
type: 'object'
),
new OA\Schema(ref: '#/components/schemas/vehicle_item_specification_v2'),
new OA\Schema(ref: '#/components/schemas/metadata_v2'),
],
)]

class HardpointItemResourceV3 extends ItemResource
{
public static function validIncludes(): array
{
return [];
}

public function toArray(Request $request): array
{
if ($this->uuid === null) {
return [];
}

$vehicleItem = $this->vehicleItem;

return [
'uuid' => $this->uuid,
'name' => $this->name,
'class_name' => $this->class_name,
'link' => $this->makeApiUrl(self::ITEMS_SHOW, $this->uuid),
'size' => $this->size,
'mass' => $this->mass,
'grade' => $vehicleItem->grade,
'class' => $vehicleItem->class,
'manufacturer' => new ManufacturerLinkResource($this->manufacturer),
'type' => $this->cleanType(),
'sub_type' => $this->sub_type,
$this->mergeWhen($this->isTurret(), $this->addTurretData()),
$this->mergeWhen(...$this->addSpecification()),
$this->mergeWhen($this->container->exists, [
'inventory' => new ItemContainerResource($this->container),
]),
'ports' => ItemPortResource::collection($this->whenLoaded('ports')),
'updated_at' => $this->updated_at,
'version' => $this->version,
];
}
}
93 changes: 93 additions & 0 deletions app/Http/Resources/SC/Vehicle/HardpointResourceV3.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

declare(strict_types=1);

namespace App\Http\Resources\SC\Vehicle;

use App\Http\Resources\AbstractBaseResource;
use Illuminate\Http\Request;
use OpenApi\Attributes as OA;

#[OA\Schema(
schema: 'hardpoint_v3',
title: 'Vehicle Port',
properties: [
new OA\Property(property: 'name', type: 'string', nullable: true),
new OA\Property(property: 'sizes', properties: [
new OA\Property(property: 'min', type: 'integer', nullable: true),
new OA\Property(property: 'max', type: 'integer', nullable: true),
], type: 'object'),
new OA\Property(property: 'class_name', type: 'string', nullable: true),
new OA\Property(property: 'health', type: 'double', nullable: true),
new OA\Property(property: 'compatible_types', ref: '#/components/schemas/item_port_type_v2', nullable: true),
new OA\Property(property: 'type', type: 'string', nullable: true),
new OA\Property(property: 'sub_type', type: 'double', nullable: true),
new OA\Property(
property: 'ports',
type: 'array',
items: new OA\Items(ref: '#/components/schemas/hardpoint_v3'),
nullable: true
),
new OA\Property(
property: 'equipped_item',
ref: '#/components/schemas/hardpoint_item_v3',
type: 'object',
nullable: true
),
],
type: 'object'
)]
class HardpointResourceV3 extends AbstractBaseResource
{
public static function validIncludes(): array
{
return [
'effects',
'item.shops',
'item.shops.items',
];
}

public function toArray(Request $request): array
{
$data = [
'name' => $this->hardpoint_name,
'position' => $this->position,
'sizes' => [
'min' => $this->min_size,
'max' => $this->max_size,
],
'class_name' => $this->class_name,
'health' => $this->item?->durabilityData?->health,
'compatible_types' => array_filter([
array_filter([
'type' => $this->item?->type,
'sub_types' => array_filter([$this->item?->sub_type]),
]),
]),
$this->mergeWhen(...$this->addItem()),
$this->mergeWhen($this->children->count() > 0, [
'ports' => self::collection($this->children),
]),
];

if (($this->item?->uuid !== null) && $this->min_size === 0) {
$data['sizes']['min'] = $this->item->size;
$data['sizes']['max'] = $this->item->size;
}

return $data;
}

private function addItem(): array
{
if (
$this->vehicleItem->exists ||
($this->item !== null && ($this->item->exists || $this->item->isTurret() || $this->item->type === 'Cargo'))
) {
return [true, ['equipped_item' => new HardpointItemResourceV3($this->item)]];
}

return [false, []];
}
}
Loading

0 comments on commit 88eeba6

Please sign in to comment.