Added simple endpoint for basic part infos and partlots

This commit is contained in:
Jan Böhmer 2023-09-03 23:58:09 +02:00
parent e04b635c98
commit 09acca950d
9 changed files with 77 additions and 28 deletions

View file

@ -27,6 +27,7 @@ use App\Services\InfoProviderSystem\DTOs\SearchResultDTO;
use Doctrine\DBAL\Types\Types; use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Embeddable; use Doctrine\ORM\Mapping\Embeddable;
use Symfony\Component\Serializer\Annotation\Groups;
/** /**
* This class represents a reference to a info provider inside a part. * This class represents a reference to a info provider inside a part.
@ -37,19 +38,23 @@ class InfoProviderReference
/** @var string|null The key referencing the provider used to get this part, or null if it was not provided by a data provider */ /** @var string|null The key referencing the provider used to get this part, or null if it was not provided by a data provider */
#[Column(type: 'string', nullable: true)] #[Column(type: 'string', nullable: true)]
#[Groups(['provider_reference:read'])]
private ?string $provider_key = null; private ?string $provider_key = null;
/** @var string|null The id of this part inside the provider system or null if the part was not provided by a data provider */ /** @var string|null The id of this part inside the provider system or null if the part was not provided by a data provider */
#[Column(type: 'string', nullable: true)] #[Column(type: 'string', nullable: true)]
#[Groups(['provider_reference:read'])]
private ?string $provider_id = null; private ?string $provider_id = null;
/** /**
* @var string|null The url of this part inside the provider system or null if this info is not existing * @var string|null The url of this part inside the provider system or null if this info is not existing
*/ */
#[Column(type: 'string', nullable: true)] #[Column(type: 'string', nullable: true)]
#[Groups(['provider_reference:read'])]
private ?string $provider_url = null; private ?string $provider_url = null;
#[Column(type: Types::DATETIME_MUTABLE, nullable: true, options: ['default' => null])] #[Column(type: Types::DATETIME_MUTABLE, nullable: true, options: ['default' => null])]
#[Groups(['provider_reference:read'])]
private ?\DateTimeInterface $last_updated = null; private ?\DateTimeInterface $last_updated = null;
/** /**

View file

@ -61,7 +61,7 @@ use Symfony\Component\Validator\Constraints as Assert;
#[ApiResource( #[ApiResource(
operations: [ operations: [
new Get(security: 'is_granted("read", object)'), new Get(security: 'is_granted("read", object)'),
new GetCollection(security: 'is_granted("@measurement_unit.read")'), new GetCollection(security: 'is_granted("@measurement_units.read")'),
new Post(securityPostDenormalize: 'is_granted("create", object)'), new Post(securityPostDenormalize: 'is_granted("create", object)'),
new Patch(security: 'is_granted("edit", object)'), new Patch(security: 'is_granted("edit", object)'),
new Delete(security: 'is_granted("delete", object)'), new Delete(security: 'is_granted("delete", object)'),
@ -73,7 +73,7 @@ use Symfony\Component\Validator\Constraints as Assert;
uriTemplate: '/footprints/{id}/children.{_format}', uriTemplate: '/footprints/{id}/children.{_format}',
operations: [ operations: [
new GetCollection(openapiContext: ['summary' => 'Retrieves the children elements of a MeasurementUnit.'], new GetCollection(openapiContext: ['summary' => 'Retrieves the children elements of a MeasurementUnit.'],
security: 'is_granted("@measurement_unit.read")') security: 'is_granted("@measurement_units.read")')
], ],
uriVariables: [ uriVariables: [
'id' => new Link(fromProperty: 'children', fromClass: MeasurementUnit::class) 'id' => new Link(fromProperty: 'children', fromClass: MeasurementUnit::class)

View file

@ -22,6 +22,15 @@ declare(strict_types=1);
namespace App\Entity\Parts; namespace App\Entity\Parts;
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Link;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Serializer\Filter\PropertyFilter;
use App\Entity\Attachments\AttachmentTypeAttachment; use App\Entity\Attachments\AttachmentTypeAttachment;
use App\Repository\PartRepository; use App\Repository\PartRepository;
use Doctrine\DBAL\Types\Types; use Doctrine\DBAL\Types\Types;
@ -60,6 +69,18 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
#[ORM\Index(name: 'parts_idx_datet_name_last_id_needs', columns: ['datetime_added', 'name', 'last_modified', 'id', 'needs_review'])] #[ORM\Index(name: 'parts_idx_datet_name_last_id_needs', columns: ['datetime_added', 'name', 'last_modified', 'id', 'needs_review'])]
#[ORM\Index(name: 'parts_idx_name', columns: ['name'])] #[ORM\Index(name: 'parts_idx_name', columns: ['name'])]
#[ORM\Index(name: 'parts_idx_ipn', columns: ['ipn'])] #[ORM\Index(name: 'parts_idx_ipn', columns: ['ipn'])]
#[ApiResource(
operations: [
new Get(security: 'is_granted("read", object)'),
new GetCollection(security: 'is_granted("@parts.read")'),
new Post(securityPostDenormalize: 'is_granted("create", object)'),
new Patch(security: 'is_granted("edit", object)'),
new Delete(security: 'is_granted("delete", object)'),
],
normalizationContext: ['groups' => ['part:read', 'provider_reference:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'],
denormalizationContext: ['groups' => ['part:write', 'api:basic:write'], 'openapi_definition_name' => 'Write'],
)]
#[ApiFilter(PropertyFilter::class)]
class Part extends AttachmentContainingDBElement class Part extends AttachmentContainingDBElement
{ {
use AdvancedPropertyTrait; use AdvancedPropertyTrait;

View file

@ -22,6 +22,14 @@ declare(strict_types=1);
namespace App\Entity\Parts; namespace App\Entity\Parts;
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Serializer\Filter\PropertyFilter;
use App\Repository\PartLotRepository; use App\Repository\PartLotRepository;
use Doctrine\DBAL\Types\Types; use Doctrine\DBAL\Types\Types;
use App\Entity\Base\AbstractDBElement; use App\Entity\Base\AbstractDBElement;
@ -50,6 +58,18 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
#[ORM\Index(name: 'part_lots_idx_instock_un_expiration_id_part', columns: ['instock_unknown', 'expiration_date', 'id_part'])] #[ORM\Index(name: 'part_lots_idx_instock_un_expiration_id_part', columns: ['instock_unknown', 'expiration_date', 'id_part'])]
#[ORM\Index(name: 'part_lots_idx_needs_refill', columns: ['needs_refill'])] #[ORM\Index(name: 'part_lots_idx_needs_refill', columns: ['needs_refill'])]
#[ValidPartLot] #[ValidPartLot]
#[ApiResource(
operations: [
new Get(security: 'is_granted("read", object)'),
new GetCollection(security: 'is_granted("@parts.read")'),
new Post(securityPostDenormalize: 'is_granted("create", object)'),
new Patch(security: 'is_granted("edit", object)'),
new Delete(security: 'is_granted("delete", object)'),
],
normalizationContext: ['groups' => ['part_lot:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'],
denormalizationContext: ['groups' => ['part_lot:write', 'api:basic:write'], 'openapi_definition_name' => 'Write'],
)]
#[ApiFilter(PropertyFilter::class)]
class PartLot extends AbstractDBElement implements TimeStampableInterface, NamedElementInterface class PartLot extends AbstractDBElement implements TimeStampableInterface, NamedElementInterface
{ {
use TimestampTrait; use TimestampTrait;
@ -57,14 +77,14 @@ class PartLot extends AbstractDBElement implements TimeStampableInterface, Named
/** /**
* @var string A short description about this lot, shown in table * @var string A short description about this lot, shown in table
*/ */
#[Groups(['simple', 'extended', 'full', 'import'])] #[Groups(['simple', 'extended', 'full', 'import', 'part_lot:read', 'part_lot:write'])]
#[ORM\Column(type: Types::TEXT)] #[ORM\Column(type: Types::TEXT)]
protected string $description = ''; protected string $description = '';
/** /**
* @var string a comment stored with this lot * @var string a comment stored with this lot
*/ */
#[Groups(['full', 'import'])] #[Groups(['full', 'import', 'part_lot:read', 'part_lot:write'])]
#[ORM\Column(type: Types::TEXT)] #[ORM\Column(type: Types::TEXT)]
protected string $comment = ''; protected string $comment = '';
@ -72,14 +92,14 @@ class PartLot extends AbstractDBElement implements TimeStampableInterface, Named
* @var \DateTimeInterface|null Set a time until when the lot must be used. * @var \DateTimeInterface|null Set a time until when the lot must be used.
* Set to null, if the lot can be used indefinitely. * Set to null, if the lot can be used indefinitely.
*/ */
#[Groups(['extended', 'full', 'import'])] #[Groups(['extended', 'full', 'import', 'part_lot:read', 'part_lot:write'])]
#[ORM\Column(type: Types::DATETIME_MUTABLE, name: 'expiration_date', nullable: true)] #[ORM\Column(type: Types::DATETIME_MUTABLE, name: 'expiration_date', nullable: true)]
protected ?\DateTimeInterface $expiration_date = null; protected ?\DateTimeInterface $expiration_date = null;
/** /**
* @var Storelocation|null The storelocation of this lot * @var Storelocation|null The storelocation of this lot
*/ */
#[Groups(['simple', 'extended', 'full', 'import'])] #[Groups(['simple', 'extended', 'full', 'import', 'part_lot:read', 'part_lot:write'])]
#[ORM\ManyToOne(targetEntity: Storelocation::class, fetch: 'EAGER')] #[ORM\ManyToOne(targetEntity: Storelocation::class, fetch: 'EAGER')]
#[ORM\JoinColumn(name: 'id_store_location')] #[ORM\JoinColumn(name: 'id_store_location')]
#[Selectable()] #[Selectable()]
@ -88,7 +108,7 @@ class PartLot extends AbstractDBElement implements TimeStampableInterface, Named
/** /**
* @var bool If this is set to true, the instock amount is marked as not known * @var bool If this is set to true, the instock amount is marked as not known
*/ */
#[Groups(['simple', 'extended', 'full', 'import'])] #[Groups(['simple', 'extended', 'full', 'import', 'part_lot:read', 'part_lot:write'])]
#[ORM\Column(type: Types::BOOLEAN)] #[ORM\Column(type: Types::BOOLEAN)]
protected bool $instock_unknown = false; protected bool $instock_unknown = false;
@ -96,14 +116,14 @@ class PartLot extends AbstractDBElement implements TimeStampableInterface, Named
* @var float For continuous sizes (length, volume, etc.) the instock is saved here. * @var float For continuous sizes (length, volume, etc.) the instock is saved here.
*/ */
#[Assert\PositiveOrZero] #[Assert\PositiveOrZero]
#[Groups(['simple', 'extended', 'full', 'import'])] #[Groups(['simple', 'extended', 'full', 'import', 'part_lot:read', 'part_lot:write'])]
#[ORM\Column(type: Types::FLOAT)] #[ORM\Column(type: Types::FLOAT)]
protected float $amount = 0.0; protected float $amount = 0.0;
/** /**
* @var bool determines if this lot was manually marked for refilling * @var bool determines if this lot was manually marked for refilling
*/ */
#[Groups(['extended', 'full', 'import'])] #[Groups(['extended', 'full', 'import', 'part_lot:read', 'part_lot:write'])]
#[ORM\Column(type: Types::BOOLEAN)] #[ORM\Column(type: Types::BOOLEAN)]
protected bool $needs_refill = false; protected bool $needs_refill = false;
@ -113,6 +133,7 @@ class PartLot extends AbstractDBElement implements TimeStampableInterface, Named
#[Assert\NotNull] #[Assert\NotNull]
#[ORM\ManyToOne(targetEntity: Part::class, inversedBy: 'partLots')] #[ORM\ManyToOne(targetEntity: Part::class, inversedBy: 'partLots')]
#[ORM\JoinColumn(name: 'id_part', nullable: false, onDelete: 'CASCADE')] #[ORM\JoinColumn(name: 'id_part', nullable: false, onDelete: 'CASCADE')]
#[Groups(['part_lot:read', 'part_lot:write'])]
protected ?Part $part = null; protected ?Part $part = null;
/** /**
@ -120,6 +141,7 @@ class PartLot extends AbstractDBElement implements TimeStampableInterface, Named
*/ */
#[ORM\ManyToOne(targetEntity: User::class)] #[ORM\ManyToOne(targetEntity: User::class)]
#[ORM\JoinColumn(name: 'id_owner', onDelete: 'SET NULL')] #[ORM\JoinColumn(name: 'id_owner', onDelete: 'SET NULL')]
#[Groups(['part_lot:read', 'part_lot:write'])]
protected ?User $owner = null; protected ?User $owner = null;
public function __clone() public function __clone()

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
namespace App\Entity\Parts\PartTraits; namespace App\Entity\Parts\PartTraits;
use ApiPlatform\Metadata\ApiProperty;
use App\Entity\Parts\InfoProviderReference; use App\Entity\Parts\InfoProviderReference;
use Doctrine\DBAL\Types\Types; use Doctrine\DBAL\Types\Types;
use App\Entity\Parts\Part; use App\Entity\Parts\Part;
@ -37,14 +38,14 @@ trait AdvancedPropertyTrait
/** /**
* @var bool Determines if this part entry needs review (for example, because it is work in progress) * @var bool Determines if this part entry needs review (for example, because it is work in progress)
*/ */
#[Groups(['extended', 'full', 'import'])] #[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])]
#[ORM\Column(type: Types::BOOLEAN)] #[ORM\Column(type: Types::BOOLEAN)]
protected bool $needs_review = false; protected bool $needs_review = false;
/** /**
* @var string a comma separated list of tags, associated with the part * @var string a comma separated list of tags, associated with the part
*/ */
#[Groups(['extended', 'full', 'import'])] #[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])]
#[ORM\Column(type: Types::TEXT)] #[ORM\Column(type: Types::TEXT)]
protected string $tags = ''; protected string $tags = '';
@ -52,7 +53,7 @@ trait AdvancedPropertyTrait
* @var float|null how much a single part unit weighs in grams * @var float|null how much a single part unit weighs in grams
*/ */
#[Assert\PositiveOrZero] #[Assert\PositiveOrZero]
#[Groups(['extended', 'full', 'import'])] #[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])]
#[ORM\Column(type: Types::FLOAT, nullable: true)] #[ORM\Column(type: Types::FLOAT, nullable: true)]
protected ?float $mass = null; protected ?float $mass = null;
@ -60,14 +61,15 @@ trait AdvancedPropertyTrait
* @var string|null The internal part number of the part * @var string|null The internal part number of the part
*/ */
#[Assert\Length(max: 100)] #[Assert\Length(max: 100)]
#[Groups(['extended', 'full', 'import'])] #[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])]
#[ORM\Column(type: Types::STRING, length: 100, nullable: true, unique: true)] #[ORM\Column(type: Types::STRING, length: 100, unique: true, nullable: true)]
protected ?string $ipn = null; protected ?string $ipn = null;
/** /**
* @var InfoProviderReference The reference to the info provider, that provided the information about this part * @var InfoProviderReference The reference to the info provider, that provided the information about this part
*/ */
#[ORM\Embedded(class: InfoProviderReference::class, columnPrefix: 'provider_reference_')] #[ORM\Embedded(class: InfoProviderReference::class, columnPrefix: 'provider_reference_')]
#[Groups(['full', 'part:read'])]
protected InfoProviderReference $providerReference; protected InfoProviderReference $providerReference;
/** /**

View file

@ -35,14 +35,14 @@ trait BasicPropertyTrait
/** /**
* @var string A text describing what this part does * @var string A text describing what this part does
*/ */
#[Groups(['simple', 'extended', 'full', 'import'])] #[Groups(['simple', 'extended', 'full', 'import', 'part:read', 'part:write'])]
#[ORM\Column(type: Types::TEXT)] #[ORM\Column(type: Types::TEXT)]
protected string $description = ''; protected string $description = '';
/** /**
* @var string A comment/note related to this part * @var string A comment/note related to this part
*/ */
#[Groups(['extended', 'full', 'import'])] #[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])]
#[ORM\Column(type: Types::TEXT)] #[ORM\Column(type: Types::TEXT)]
protected string $comment = ''; protected string $comment = '';
@ -55,7 +55,7 @@ trait BasicPropertyTrait
/** /**
* @var bool true, if the part is marked as favorite * @var bool true, if the part is marked as favorite
*/ */
#[Groups(['extended', 'full', 'import'])] #[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])]
#[ORM\Column(type: Types::BOOLEAN)] #[ORM\Column(type: Types::BOOLEAN)]
protected bool $favorite = false; protected bool $favorite = false;
@ -65,7 +65,7 @@ trait BasicPropertyTrait
*/ */
#[Assert\NotNull(message: 'validator.select_valid_category')] #[Assert\NotNull(message: 'validator.select_valid_category')]
#[Selectable()] #[Selectable()]
#[Groups(['simple', 'extended', 'full', 'import'])] #[Groups(['simple', 'extended', 'full', 'import', "part:read", "part:write"])]
#[ORM\ManyToOne(targetEntity: Category::class)] #[ORM\ManyToOne(targetEntity: Category::class)]
#[ORM\JoinColumn(name: 'id_category', nullable: false)] #[ORM\JoinColumn(name: 'id_category', nullable: false)]
protected ?Category $category = null; protected ?Category $category = null;
@ -73,7 +73,7 @@ trait BasicPropertyTrait
/** /**
* @var Footprint|null The footprint of this part (e.g. DIP8) * @var Footprint|null The footprint of this part (e.g. DIP8)
*/ */
#[Groups(['simple', 'extended', 'full', 'import'])] #[Groups(['simple', 'extended', 'full', 'import', 'part:read', 'part:write'])]
#[ORM\ManyToOne(targetEntity: Footprint::class)] #[ORM\ManyToOne(targetEntity: Footprint::class)]
#[ORM\JoinColumn(name: 'id_footprint')] #[ORM\JoinColumn(name: 'id_footprint')]
#[Selectable()] #[Selectable()]

View file

@ -49,14 +49,14 @@ trait InstockTrait
* Given in the partUnit. * Given in the partUnit.
*/ */
#[Assert\PositiveOrZero] #[Assert\PositiveOrZero]
#[Groups(['extended', 'full', 'import'])] #[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])]
#[ORM\Column(type: Types::FLOAT)] #[ORM\Column(type: Types::FLOAT)]
protected float $minamount = 0; protected float $minamount = 0;
/** /**
* @var ?MeasurementUnit the unit in which the part's amount is measured * @var ?MeasurementUnit the unit in which the part's amount is measured
*/ */
#[Groups(['extended', 'full', 'import'])] #[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])]
#[ORM\ManyToOne(targetEntity: MeasurementUnit::class)] #[ORM\ManyToOne(targetEntity: MeasurementUnit::class)]
#[ORM\JoinColumn(name: 'id_part_unit')] #[ORM\JoinColumn(name: 'id_part_unit')]
protected ?MeasurementUnit $partUnit = null; protected ?MeasurementUnit $partUnit = null;

View file

@ -39,7 +39,7 @@ trait ManufacturerTrait
/** /**
* @var Manufacturer|null The manufacturer of this part * @var Manufacturer|null The manufacturer of this part
*/ */
#[Groups(['simple', 'extended', 'full', 'import'])] #[Groups(['simple', 'extended', 'full', 'import', 'part:read', 'part:write'])]
#[ORM\ManyToOne(targetEntity: Manufacturer::class)] #[ORM\ManyToOne(targetEntity: Manufacturer::class)]
#[ORM\JoinColumn(name: 'id_manufacturer')] #[ORM\JoinColumn(name: 'id_manufacturer')]
#[Selectable()] #[Selectable()]
@ -49,21 +49,21 @@ trait ManufacturerTrait
* @var string the url to the part on the manufacturer's homepage * @var string the url to the part on the manufacturer's homepage
*/ */
#[Assert\Url] #[Assert\Url]
#[Groups(['full', 'import'])] #[Groups(['full', 'import', 'part:read', 'part:write'])]
#[ORM\Column(type: Types::TEXT)] #[ORM\Column(type: Types::TEXT)]
protected string $manufacturer_product_url = ''; protected string $manufacturer_product_url = '';
/** /**
* @var string The product number used by the manufacturer. If this is set to "", the name field is used. * @var string The product number used by the manufacturer. If this is set to "", the name field is used.
*/ */
#[Groups(['extended', 'full', 'import'])] #[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])]
#[ORM\Column(type: Types::STRING)] #[ORM\Column(type: Types::STRING)]
protected string $manufacturer_product_number = ''; protected string $manufacturer_product_number = '';
/** /**
* @var ManufacturingStatus|null The production status of this part. Can be one of the specified ones. * @var ManufacturingStatus|null The production status of this part. Can be one of the specified ones.
*/ */
#[Groups(['extended', 'full', 'import'])] #[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])]
#[ORM\Column(type: Types::STRING, length: 255, nullable: true, enumType: ManufacturingStatus::class)] #[ORM\Column(type: Types::STRING, length: 255, nullable: true, enumType: ManufacturingStatus::class)]
protected ?ManufacturingStatus $manufacturing_status = ManufacturingStatus::NOT_SET; protected ?ManufacturingStatus $manufacturing_status = ManufacturingStatus::NOT_SET;

View file

@ -9,12 +9,10 @@ use App\Entity\ProjectSystem\ProjectBOMEntry;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
trait ProjectTrait trait ProjectTrait
{ {
/**
* @var Collection<ProjectBOMEntry> $project_bom_entries
*/
/** /**
* @var Collection<ProjectBOMEntry> $project_bom_entries * @var Collection<ProjectBOMEntry> $project_bom_entries
*/ */
@ -42,6 +40,7 @@ trait ProjectTrait
* Checks whether this part represents the builds of a project * Checks whether this part represents the builds of a project
* @return bool True if it represents the builds, false if not * @return bool True if it represents the builds, false if not
*/ */
#[Groups(['part:read'])]
public function isProjectBuildPart(): bool public function isProjectBuildPart(): bool
{ {
return $this->built_project !== null; return $this->built_project !== null;