diff --git a/config/packages/lexik_jwt_authentication.yaml b/config/packages/lexik_jwt_authentication.yaml index c57cefa..5605f77 100644 --- a/config/packages/lexik_jwt_authentication.yaml +++ b/config/packages/lexik_jwt_authentication.yaml @@ -3,4 +3,5 @@ lexik_jwt_authentication: public_key: '%env(resolve:JWT_PUBLIC_KEY)%' pass_phrase: '%env(JWT_PASSPHRASE)%' allow_no_expiration: true + # token_ttl: 0 # token_ttl: 30000000 \ No newline at end of file diff --git a/migrations/Version20230911215906.php b/migrations/Version20230911215906.php new file mode 100644 index 0000000..36ca161 --- /dev/null +++ b/migrations/Version20230911215906.php @@ -0,0 +1,37 @@ +addSql('CREATE TABLE shipment_budget (id BINARY(16) NOT NULL COMMENT \'(DC2Type:ulid)\', currency VARCHAR(3) NOT NULL, dtype VARCHAR(255) NOT NULL, price INT DEFAULT NULL, min_price INT DEFAULT NULL, max_price INT DEFAULT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE shipment ADD budget_id BINARY(16) DEFAULT NULL COMMENT \'(DC2Type:ulid)\''); + $this->addSql('ALTER TABLE shipment ADD CONSTRAINT FK_2CB20DC36ABA6B8 FOREIGN KEY (budget_id) REFERENCES shipment_budget (id)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_2CB20DC36ABA6B8 ON shipment (budget_id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE shipment DROP FOREIGN KEY FK_2CB20DC36ABA6B8'); + $this->addSql('DROP TABLE shipment_budget'); + $this->addSql('DROP INDEX UNIQ_2CB20DC36ABA6B8 ON shipment'); + $this->addSql('ALTER TABLE shipment DROP budget_id'); + } +} diff --git a/migrations/Version20230911220220.php b/migrations/Version20230911220220.php new file mode 100644 index 0000000..365a299 --- /dev/null +++ b/migrations/Version20230911220220.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE shipment ADD updated_at DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', ADD created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\''); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE shipment DROP updated_at, DROP created_at'); + } +} diff --git a/migrations/Version20230911234025.php b/migrations/Version20230911234025.php new file mode 100644 index 0000000..f0a484a --- /dev/null +++ b/migrations/Version20230911234025.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE shipment ADD type VARCHAR(64) NOT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE shipment DROP type'); + } +} diff --git a/src/Entity/Account/User.php b/src/Entity/Account/User.php index 9552271..b71d92c 100644 --- a/src/Entity/Account/User.php +++ b/src/Entity/Account/User.php @@ -28,9 +28,14 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface #[ORM\CustomIdGenerator(class: 'doctrine.ulid_generator')] private ?Ulid $id = null; + #[GQL\Field()] #[ORM\Column(length: 180, unique: true)] private ?string $email = null; + /** + * @var string[] + */ + #[GQL\Field()] #[ORM\Column] private array $roles = []; @@ -40,21 +45,26 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface #[ORM\Column] private ?string $password = null; + #[GQL\Field()] #[ORM\Column(length: 64)] private ?string $firstName = null; + #[GQL\Field()] #[ORM\Column(length: 64)] private ?string $lastName = null; + #[GQL\Field()] #[ORM\Column(length: 64, nullable: true)] private ?string $phone = null; + #[GQL\Field()] #[ORM\Column(type: 'boolean')] - private $isVerified = false; + private bool $isVerified = false; #[ORM\OneToMany(mappedBy: 'owner', targetEntity: UserAddress::class, orphanRemoval: true)] private Collection $addressess; + #[GQL\Field()] #[ORM\OneToOne(mappedBy: 'userAccount', cascade: ['persist', 'remove'])] private ?Driver $driver = null; @@ -99,6 +109,10 @@ public function getRoles(): array // guarantee every user at least has ROLE_USER $roles[] = 'ROLE_USER'; + if($this->driver != null){ + $roles[] = 'ROLE_DRIVER'; + } + return array_unique($roles); } diff --git a/src/Entity/Shipment/Shipment.php b/src/Entity/Shipment/Shipment.php index 8dc690b..83b1676 100644 --- a/src/Entity/Shipment/Shipment.php +++ b/src/Entity/Shipment/Shipment.php @@ -23,36 +23,54 @@ class Shipment #[ORM\CustomIdGenerator(class: 'doctrine.ulid_generator')] private ?Ulid $id = null; + #[GQL\Field()] + #[ORM\Column(length: 64, enumType: ShipmentType::class)] + private ?ShipmentType $type = null; + #[GQL\Field()] #[ORM\ManyToOne] #[ORM\JoinColumn(nullable: false)] private ?User $owner = null; #[GQL\Field()] - #[ORM\ManyToOne] + #[ORM\ManyToOne(cascade: ['persist',])] private ?UserAddress $billingAddress = null; #[GQL\Field()] - #[ORM\ManyToOne] + #[ORM\ManyToOne(cascade: ['persist',])] private ?UserAddress $originAddress = null; #[GQL\Field()] - #[ORM\ManyToOne] + #[ORM\ManyToOne(cascade: ['persist',])] private ?UserAddress $destinationAddress = null; #[GQL\Field(type:'[ShipmentItem!]!')] - #[ORM\OneToMany(mappedBy: 'shipment', targetEntity: ShipmentItem::class, orphanRemoval: true)] + #[ORM\OneToMany(mappedBy: 'shipment', targetEntity: ShipmentItem::class, cascade: ['persist', 'remove'], orphanRemoval: true)] private Collection $items; #[GQL\Field(type:'[ShipmentDriverBid!]!')] - #[ORM\OneToMany(mappedBy: 'shipment', targetEntity: ShipmentDriverBid::class, orphanRemoval: true)] + #[ORM\OneToMany(mappedBy: 'shipment', targetEntity: ShipmentDriverBid::class, cascade: ['persist', 'remove'], orphanRemoval: true)] private Collection $bids; + #[GQL\Field()] + #[ORM\OneToOne(inversedBy: 'shipment', cascade: ['persist', 'remove'])] + private ?ShipmentBudget $budget = null; + + #[GQL\Field(type: "DateTime")] + #[ORM\Column(nullable: true)] + private ?\DateTimeImmutable $updatedAt = null; + + #[GQL\Field(type: "DateTime")] + #[ORM\Column()] + private ?\DateTimeImmutable $createdAt = null; + + public function __construct(?Ulid $id = null) { $this->id = $id; $this->items = new ArrayCollection(); $this->bids = new ArrayCollection(); + $this->createdAt = new \DateTimeImmutable(); } public function getId(): ?Ulid @@ -60,6 +78,19 @@ public function getId(): ?Ulid return $this->id; } + + public function getType(): ?ShipmentType + { + return $this->type; + } + + public function setType(ShipmentType $type): static + { + $this->type = $type; + + return $this; + } + public function getOwner(): ?User { return $this->owner; @@ -167,4 +198,41 @@ public function removeBid(ShipmentDriverBid $bid): static return $this; } + + public function getBudget(): ?ShipmentBudget + { + return $this->budget; + } + + public function setBudget(?ShipmentBudget $budget): static + { + $this->budget = $budget; + + return $this; + } + + public function getUpdatedAt(): ?\DateTimeImmutable + { + return $this->updatedAt; + } + + public function setUpdatedAt(?\DateTimeImmutable $updatedAt): static + { + $this->updatedAt = $updatedAt; + + return $this; + } + + public function getCreatedAt(): ?\DateTimeImmutable + { + return $this->createdAt; + } + + public function setCreatedAt(?\DateTimeImmutable $createdAt): static + { + $this->createdAt = $createdAt; + + return $this; + } + } diff --git a/src/Entity/Shipment/ShipmentBudget.php b/src/Entity/Shipment/ShipmentBudget.php new file mode 100644 index 0000000..748ddee --- /dev/null +++ b/src/Entity/Shipment/ShipmentBudget.php @@ -0,0 +1,72 @@ +id; + } + + public function getCurrency(): ?string + { + return $this->currency; + } + + public function setCurrency(string $currency): static + { + $this->currency = $currency; + + return $this; + } + + abstract function resolveGQLType( TypeResolver $typeResolver): Type; + + public function getShipment(): ?Shipment + { + return $this->shipment; + } + + public function setShipment(?Shipment $shipment): static + { + // unset the owning side of the relation if necessary + if ($shipment === null && $this->shipment !== null) { + $this->shipment->setBudget(null); + } + + // set the owning side of the relation if necessary + if ($shipment !== null && $shipment->getBudget() !== $this) { + $shipment->setBudget($this); + } + + $this->shipment = $shipment; + + return $this; + } +} diff --git a/src/Entity/Shipment/ShipmentFixedBudget.php b/src/Entity/Shipment/ShipmentFixedBudget.php new file mode 100644 index 0000000..1ea8c2e --- /dev/null +++ b/src/Entity/Shipment/ShipmentFixedBudget.php @@ -0,0 +1,38 @@ +price; + } + + public function setPrice(int $price): static + { + $this->price = $price; + + return $this; + } + + public function resolveGQLType(TypeResolver $typeResolver): Type{ + return $typeResolver->resolve('ShipmentFixedBudget'); + } +} diff --git a/src/Entity/Shipment/ShipmentItem.php b/src/Entity/Shipment/ShipmentItem.php index 49f1bf0..c4ad7c5 100644 --- a/src/Entity/Shipment/ShipmentItem.php +++ b/src/Entity/Shipment/ShipmentItem.php @@ -49,6 +49,12 @@ class ShipmentItem #[ORM\Column] private ?\DateTimeImmutable $createdAt = null; + + public function __construct(?Ulid $id = null){ + $this->id = $id; + $this->createdAt = new \DateTimeImmutable(); + } + public function getId(): ?Ulid { return $this->id; diff --git a/src/Entity/Shipment/ShipmentRangeBudget.php b/src/Entity/Shipment/ShipmentRangeBudget.php new file mode 100644 index 0000000..a803864 --- /dev/null +++ b/src/Entity/Shipment/ShipmentRangeBudget.php @@ -0,0 +1,52 @@ + value.minPrice")] +#[GQL\Type()] +#[ORM\Entity(repositoryClass: ShipmentRangeBudgetRepository::class)] +class ShipmentRangeBudget extends ShipmentBudget +{ + #[GQL\Field()] + #[ORM\Column(nullable: true)] + private ?int $minPrice = null; + + #[GQL\Field()] + #[ORM\Column(nullable: true)] + private ?int $maxPrice = null; + + public function getMinPrice(): ?int + { + return $this->minPrice; + } + + public function setMinPrice(int $minPrice): static + { + $this->minPrice = $minPrice; + + return $this; + } + + public function getMaxPrice(): ?int + { + return $this->maxPrice; + } + + public function setMaxPrice(int $maxPrice): static + { + $this->maxPrice = $maxPrice; + + return $this; + } + + public function resolveGQLType(TypeResolver $typeResolver): Type{ + return $typeResolver->resolve('ShipmentRangeBudget'); + } +} diff --git a/src/GraphQL/Account/Input/AdminUserInput.php b/src/GraphQL/Account/Input/AdminUserInput.php new file mode 100644 index 0000000..23c664c --- /dev/null +++ b/src/GraphQL/Account/Input/AdminUserInput.php @@ -0,0 +1,12 @@ +setTitle($this->title) + // ->setDescription($this->description) + ; + } +} diff --git a/src/GraphQL/Account/Mutation/UserMutationResolver.php b/src/GraphQL/Account/Mutation/UserMutationResolver.php new file mode 100644 index 0000000..bdf7dfa --- /dev/null +++ b/src/GraphQL/Account/Mutation/UserMutationResolver.php @@ -0,0 +1,40 @@ +security->getUser(); + if(!($user instanceof User)){ + throw new UserError("Permission Denied: You may not perform this operation"); + } + + $user = new User(); + $input->build($user); + + $this->entityManager->persist($user); + $this->entityManager->flush(); + + return $user; + } +} diff --git a/src/GraphQL/Account/Query/UserQueryResolver.php b/src/GraphQL/Account/Query/UserQueryResolver.php new file mode 100644 index 0000000..9e24fe9 --- /dev/null +++ b/src/GraphQL/Account/Query/UserQueryResolver.php @@ -0,0 +1,102 @@ +security->getUser(); + if (!($user instanceof User)) { + throw new UserError( + message: "Cannot find current user" + ); + } + // if (!$this->security->isGranted('view', $user)) { + // throw new UserError( + // message: "Permision Denied: You may not view this resource" + // ); + // } + return $user; + } + + #[Query(name: "get_user_item",)] + #[GQL\Arg( + name: 'id', + type: 'Ulid' + )] + public function getUserItem( + #[GQL\Arg(type: 'Ulid')] Ulid $id + ): User { + + $user = $this->userRepository->find($id); + if ($user === null) { + throw new UserError( + message: "Cannot find user with [id:$id]" + ); + } + + if (!$this->security->isGranted('view', $user)) { + throw new UserError( + message: "Permision Denied: You may not view this resource" + ); + } + + return $user; + } + + #[GQL\Query(name: "get_user_list")] + public function getUserConnection( + ?int $first, + ?String $after, + ?String $filter, + ?String $sort, + ): UserConnection { + + + $cb = new ConnectionBuilder( + null, + fn ($edges, PageInfoInterface $pageInfo) => new UserConnection($edges, $pageInfo), + fn (string $coursor, User $user, int $index) => new UserEdge($coursor, $user) + ); + + $qb = $this->userRepository->createQueryBuilder('user'); + QueryBuilderHelper::applyCriteria($qb, $filter, 'user'); + + $total = fn () => (int) (clone $qb)->select('COUNT(user.id)')->getQuery()->getSingleScalarResult(); + $paginator = new Paginator(function (?int $offset, ?int $limit) use ($qb) { + return $qb->getQuery()->getResult(); + }, false, $cb); + + return $paginator->auto(new Argument(['first' => $first, 'after' => $after]), $total); + } +} diff --git a/src/GraphQL/Account/Type/UserConnection.php b/src/GraphQL/Account/Type/UserConnection.php new file mode 100644 index 0000000..0261140 --- /dev/null +++ b/src/GraphQL/Account/Type/UserConnection.php @@ -0,0 +1,11 @@ +setGoogleId($this->googleId) ->setFirstName($this->firstName) ->setLastName($this->lastName) ->setPhoneNumber($this->phoneNumber) diff --git a/src/GraphQL/Addressing/Mutation/AddressMutationResolver.php b/src/GraphQL/Addressing/Mutation/AddressMutationResolver.php index 31f7715..d8e6fe1 100644 --- a/src/GraphQL/Addressing/Mutation/AddressMutationResolver.php +++ b/src/GraphQL/Addressing/Mutation/AddressMutationResolver.php @@ -28,7 +28,7 @@ public function createNewAddress(AddressCreationInput $input): Address{ $user = $this->security->getUser(); if(!($user instanceof User)){ - throw new UserError("Permission Denied: You may not perform the said operation"); + throw new UserError("Permission Denied: You may not perform this operation"); } $address = new UserAddress(); $input->build($address); diff --git a/src/GraphQL/Catalog/Mutation/ProductMutationResolver.php b/src/GraphQL/Catalog/Mutation/ProductMutationResolver.php index 7d650be..85ba99f 100644 --- a/src/GraphQL/Catalog/Mutation/ProductMutationResolver.php +++ b/src/GraphQL/Catalog/Mutation/ProductMutationResolver.php @@ -28,7 +28,7 @@ public function createNewProduct(ProductCreationInput $input): Product{ $user = $this->security->getUser(); if(!($user instanceof User)){ - throw new UserError("Permission Denied: You may not perform the said operation"); + throw new UserError("Permission Denied: You may not perform this operation"); } $product = new UserProduct(); diff --git a/src/GraphQL/Shipment/Input/ShipmentCreationInput.php b/src/GraphQL/Shipment/Input/ShipmentCreationInput.php new file mode 100644 index 0000000..7759598 --- /dev/null +++ b/src/GraphQL/Shipment/Input/ShipmentCreationInput.php @@ -0,0 +1,8 @@ + + * + * @method ShipmentBudget|null find($id, $lockMode = null, $lockVersion = null) + * @method ShipmentBudget|null findOneBy(array $criteria, array $orderBy = null) + * @method ShipmentBudget[] findAll() + * @method ShipmentBudget[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class ShipmentBudgetRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, ShipmentBudget::class); + } + +// /** +// * @return ShipmentBudget[] Returns an array of ShipmentBudget objects +// */ +// public function findByExampleField($value): array +// { +// return $this->createQueryBuilder('s') +// ->andWhere('s.exampleField = :val') +// ->setParameter('val', $value) +// ->orderBy('s.id', 'ASC') +// ->setMaxResults(10) +// ->getQuery() +// ->getResult() +// ; +// } + +// public function findOneBySomeField($value): ?ShipmentBudget +// { +// return $this->createQueryBuilder('s') +// ->andWhere('s.exampleField = :val') +// ->setParameter('val', $value) +// ->getQuery() +// ->getOneOrNullResult() +// ; +// } +} diff --git a/src/Repository/Shipment/ShipmentFixedBudgetRepository.php b/src/Repository/Shipment/ShipmentFixedBudgetRepository.php new file mode 100644 index 0000000..fd6f3b7 --- /dev/null +++ b/src/Repository/Shipment/ShipmentFixedBudgetRepository.php @@ -0,0 +1,48 @@ + + * + * @method ShipmentFixedBudget|null find($id, $lockMode = null, $lockVersion = null) + * @method ShipmentFixedBudget|null findOneBy(array $criteria, array $orderBy = null) + * @method ShipmentFixedBudget[] findAll() + * @method ShipmentFixedBudget[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class ShipmentFixedBudgetRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, ShipmentFixedBudget::class); + } + +// /** +// * @return ShipmentFixedBudget[] Returns an array of ShipmentFixedBudget objects +// */ +// public function findByExampleField($value): array +// { +// return $this->createQueryBuilder('s') +// ->andWhere('s.exampleField = :val') +// ->setParameter('val', $value) +// ->orderBy('s.id', 'ASC') +// ->setMaxResults(10) +// ->getQuery() +// ->getResult() +// ; +// } + +// public function findOneBySomeField($value): ?ShipmentFixedBudget +// { +// return $this->createQueryBuilder('s') +// ->andWhere('s.exampleField = :val') +// ->setParameter('val', $value) +// ->getQuery() +// ->getOneOrNullResult() +// ; +// } +} diff --git a/src/Repository/Shipment/ShipmentRangeBudgetRepository.php b/src/Repository/Shipment/ShipmentRangeBudgetRepository.php new file mode 100644 index 0000000..5f538ad --- /dev/null +++ b/src/Repository/Shipment/ShipmentRangeBudgetRepository.php @@ -0,0 +1,48 @@ + + * + * @method ShipmentRangeBudget|null find($id, $lockMode = null, $lockVersion = null) + * @method ShipmentRangeBudget|null findOneBy(array $criteria, array $orderBy = null) + * @method ShipmentRangeBudget[] findAll() + * @method ShipmentRangeBudget[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class ShipmentRangeBudgetRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, ShipmentRangeBudget::class); + } + +// /** +// * @return ShipmentRangeBudget[] Returns an array of ShipmentRangeBudget objects +// */ +// public function findByExampleField($value): array +// { +// return $this->createQueryBuilder('s') +// ->andWhere('s.exampleField = :val') +// ->setParameter('val', $value) +// ->orderBy('s.id', 'ASC') +// ->setMaxResults(10) +// ->getQuery() +// ->getResult() +// ; +// } + +// public function findOneBySomeField($value): ?ShipmentRangeBudget +// { +// return $this->createQueryBuilder('s') +// ->andWhere('s.exampleField = :val') +// ->setParameter('val', $value) +// ->getQuery() +// ->getOneOrNullResult() +// ; +// } +}