diff -u b/alexanders.module b/alexanders.module --- b/alexanders.module +++ b/alexanders.module @@ -1,0 +2,5 @@ + +/** + * @file + * This file left intentionally blank. + */ diff -u b/modules/commerce_alexanders/commerce_alexanders.module b/modules/commerce_alexanders/commerce_alexanders.module --- b/modules/commerce_alexanders/commerce_alexanders.module +++ b/modules/commerce_alexanders/commerce_alexanders.module @@ -1,30 +1,34 @@ getState(); // @TODO make this configurable. if ($state->getId() === 'completed') { $alexanders_order_items = []; foreach ($order->getItems() as $key => $item) { $product = $item->getPurchasedEntity()->getProduct(); - if ($product->hasField('field_alexanders_send')) { + if ($product->hasField('alxdr_send')) { $alexanders_order_items[] = AlexandersOrderItem::create([ 'sku' => $item->getPurchasedEntity()->get('sku')->getValue()[0]['value'], 'quantity' => $item->getQuantity(), -// 'file' => $item->get('field_file_url')->getValue()[0], -// 'foil' => $item->get('field_alexanders_foil_url')->getValue()[0], - 'file' => 'example.com', - 'foil' => 'example.com', + 'file' => $item->get('field_file_url')->getValue()[0], + 'foil' => $item->get('field_alexanders_foil_url')->getValue()[0], ]); } } @@ -37,7 +41,7 @@ $alexanders_order = AlexandersOrder::create([ 'order_number' => $order->id(), - 'rush' => FALSE, + 'rush' => $order->hasField('alxdr_rush') && $order->get('alxdr_rush')->value, 'orderItems' => $alexanders_order_items, 'shipping' => $alexanders_shipment, ]); diff -u b/modules/commerce_alexanders/commerce_alexanders.routing.yml b/modules/commerce_alexanders/commerce_alexanders.routing.yml --- b/modules/commerce_alexanders/commerce_alexanders.routing.yml +++ b/modules/commerce_alexanders/commerce_alexanders.routing.yml @@ -7 +7 @@ - _permission: 'administer site configuration' + _permission: 'manage alexanders printing api' diff -u b/modules/commerce_alexanders/src/Form/CommerceAlexandersManagementForm.php b/modules/commerce_alexanders/src/Form/CommerceAlexandersManagementForm.php --- b/modules/commerce_alexanders/src/Form/CommerceAlexandersManagementForm.php +++ b/modules/commerce_alexanders/src/Form/CommerceAlexandersManagementForm.php @@ -47,7 +47,7 @@ $selected = $config->get('product_types'); $form['product_types'] = [ '#title' => $this->t('Product Types'), - '#description' => $this->t('Send Alexander these product types.'), + '#description' => $this->t('Send Alexanders orders that contain these types of products.'), '#type' => 'checkboxes', '#options' => $product_types_formatted, '#default_value' => $selected, @@ -70,7 +70,7 @@ // Define the fields we need to add here, so we can loop instead of // repeating code. $neededFields = [ - 'field_alexanders_send' => $this->t('Send to Alexanders API'), + 'alxdr_send' => $this->t('Send to Alexanders API'), ]; foreach ($values['product_types'] as $key => $value) { foreach ($neededFields as $neededField => $label) { diff -u b/src/Client/AlexandersApi.php b/src/Client/AlexandersApi.php --- b/src/Client/AlexandersApi.php +++ b/src/Client/AlexandersApi.php @@ -2,8 +2,14 @@ namespace Drupal\alexanders\Client; -use Drupal\commerce_order\Entity\Order; +use Drupal\alexanders\Entity\AlexandersOrder; +use GuzzleHttp\Psr7\Response; +/** + * Defines functions for passing data to the Alexanders API. + * + * @package Drupal\alexanders\Client + */ class AlexandersApi { private $sandbox; @@ -33,12 +39,12 @@ * Sent a POST request to the API to create an order. * * @param \Drupal\alexanders\Entity\AlexandersOrder $order - * ID of order to send. + * AlexandersOrder to send. * * @return bool * TRUE if we created the order, FALSE otherwise. */ - public function createOrder($order) { + public function createOrder(AlexandersOrder $order) { $orderData = $this->buildOrderData($order); $url = $this->generateUrl(); try { @@ -54,15 +60,15 @@ /** * Sent a POST request to the API to create an order. * - * @param int $order_id - * ID of order to send. + * @param \Drupal\alexanders\Entity\AlexandersOrder $order + * AlexandersOrder to send. * * @return bool * TRUE if we created the order, FALSE otherwise. */ - public function updateOrder($order_id) { - $orderData = $this->buildOrderData($order_id, TRUE); - $url = $this->generateUrl($order_id); + public function updateOrder(AlexandersOrder $order) { + $orderData = $this->buildOrderData($order, TRUE); + $url = $this->generateUrl($order); try { $response = $this->client->put($url, $orderData); } @@ -99,11 +105,13 @@ * * @param \Drupal\alexanders\Entity\AlexandersOrder $order * Order ID to build request data off of. + * @param bool $only_shipping + * Whether to only return the address for order updates. * * @return array|bool * Return array if successful, FALSE if order can't be loaded. */ - protected function buildOrderData($order, $only_shipping = FALSE) { + protected function buildOrderData(AlexandersOrder $order, $only_shipping = FALSE) { $data = [ 'orderKey1' => $order->id(), 'orderKey2' => $order->label(), @@ -119,7 +127,7 @@ 'sku' => $item->getSku(), 'quantity' => $item->getQuantity(), 'fileUrl' => $item->getFile(), - 'foilUrl' => $item->getAddFile() + 'foilUrl' => $item->getAddFile(), ]; } $shipment = $order->getShipment()[0]; @@ -143,7 +151,7 @@ * @return bool * TRUE if the request was a success, FALSE otherwise. */ - protected function processResponse($response) { + protected function processResponse(Response $response) { if ($response->getStatusCode() == 200) { return TRUE; } @@ -154,15 +162,18 @@ /** * Determine if we're using a sandbox or otherwise and give correct URL. * + * @param \Drupal\alexanders\Entity\AlexandersOrder $order + * Order entity to build URL for. + * * @return string * URL to send requests to. */ - private function generateUrl($id = FALSE) { + private function generateUrl(AlexandersOrder $order = NULL) { if ($this->sandbox || !$this->key) { $this->baseUrl = 'https://devapi.divvy.systems/v1.0/order'; } - if ($id === FALSE) { - $this->baseUrl .= '/' . $id; + if ($order !== NULL) { + $this->baseUrl .= '/' . $order->id(); } return $this->baseUrl; } diff -u b/src/Controller/ApiController.php b/src/Controller/ApiController.php --- b/src/Controller/ApiController.php +++ b/src/Controller/ApiController.php @@ -7,8 +7,26 @@ use Symfony\Component\HttpFoundation\Request; use Drupal\alexanders\Entity\AlexandersOrder; +/** + * Handles incoming API requests from Alexanders. + * + * @package Drupal\alexanders\Controller + */ class ApiController extends ControllerBase { + /** + * Updates 'due date' parameter of Alexanders order. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * Incoming request object. + * @param int $order_id + * Order ID. + * + * @return \Symfony\Component\HttpFoundation\Response + * Returns status code based on result. + * + * @throws \Drupal\Core\Entity\EntityStorageException + */ public function orderPrintingUpdate(Request $request, $order_id) { $response = new Response(); $order = AlexandersOrder::load($order_id); @@ -42,6 +60,19 @@ return $response; } + /** + * Updates order shipment status. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * Incoming request object. + * @param int $order_id + * Order ID. + * + * @return \Symfony\Component\HttpFoundation\Response + * Returns status code based on result. + * + * @throws \Drupal\Core\Entity\EntityStorageException + */ public function orderShippedUpdate(Request $request, $order_id) { $response = new Response(); $order = AlexandersOrder::load($order_id); @@ -78,6 +109,19 @@ return $response; } + /** + * Logs an error associated with the item for manual review. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * Incoming request object. + * @param int $order_id + * Order ID. + * + * @return \Symfony\Component\HttpFoundation\Response + * Returns status code based on result. + * + * @throws \Drupal\Core\Entity\EntityStorageException + */ public function orderErrorUpdate(Request $request, $order_id) { $response = new Response(); $order = AlexandersOrder::load($order_id); @@ -115,6 +159,8 @@ } /** + * Validate API key. + * * @param string $api_key * API key passed to the API. * diff -u b/src/Entity/AlexandersOrder.php b/src/Entity/AlexandersOrder.php --- b/src/Entity/AlexandersOrder.php +++ b/src/Entity/AlexandersOrder.php @@ -33,51 +33,84 @@ */ class AlexandersOrder extends ContentEntityBase implements AlexandersOrderInterface { + /** + * {@inheritdoc} + */ public function getItems() { return $this->get('orderItems')->referencedEntities(); } + /** + * {@inheritdoc} + */ public function setItems($items) { $this->set('orderItems', $items); return $this; } + /** + * {@inheritdoc} + */ public function getPhotobooks() { return $this->get('photobookItems')->referencedEntities(); } + /** + * {@inheritdoc} + */ public function setPhotobooks($photobooks) { $this->set('photobookItems', $photobooks); return $this; } + /** + * {@inheritdoc} + */ public function getShipment() { return $this->get('shipping')->referencedEntities(); } + /** + * {@inheritdoc} + */ public function setShipment($shipping) { $this->set('shipping', $shipping); return $this; } + /** + * {@inheritdoc} + */ public function getRush() { return $this->get('rush')->value; } + /** + * {@inheritdoc} + */ public function setRush($rush) { $this->set('rush', $rush); return $this; } + /** + * {@inheritdoc} + */ public function getDue() { return $this->get('dueDate')->value; } + /** + * {@inheritdoc} + */ public function setDue($due) { $this->set('dueDate', $due); return $this; } + /** + * {@inheritdoc} + */ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields = parent::baseFieldDefinitions($entity_type); diff -u b/src/Entity/AlexandersOrderInterface.php b/src/Entity/AlexandersOrderInterface.php --- b/src/Entity/AlexandersOrderInterface.php +++ b/src/Entity/AlexandersOrderInterface.php @@ -4,6 +4,11 @@ use Drupal\Core\Entity\ContentEntityInterface; +/** + * Defines function for AlexandersOrder entities. + * + * @package Drupal\alexanders\Entity + */ interface AlexandersOrderInterface extends ContentEntityInterface { /** @@ -19,8 +24,10 @@ * * @param array $items * Array of orderItems. + * + * @return $this */ - public function setItems($items); + public function setItems(array $items); /** * Get photobooks associated with the order. @@ -35,31 +42,63 @@ * * @param array $photobooks * Array of orderItems. + * + * @return $this */ - public function setPhotobooks($photobooks); + public function setPhotobooks(array $photobooks); /** - * {@inheritdoc} + * Get the shipment object associated with the order. + * + * @return object + * AlexandersShipment entity. */ public function getShipment(); /** - * {@inheritdoc} + * Sets the shipping method for the order. + * + * @param object $shipping + * AlexandersShipment entity to associate with order. + * + * @return $this */ public function setShipment($shipping); /** - * {@inheritdoc} + * Gets the rush status of the order. + * + * @return bool + * Whether or not order is a rush. */ public function getRush(); /** - * {@inheritdoc} + * Set rush status of order. + * + * @param bool $rush + * Whether this order is actually a rush item. + * + * @return $this */ public function setRush($rush); + /** + * Get due date for order (e.g when it should be shipped). + * + * @return int + * Due date of this particular order. + */ public function getDue(); + /** + * Set due date of order. + * + * @param int $due + * Epoch timestamp of when this order is due. + * + * @return $this + */ public function setDue($due); } diff -u b/src/Entity/AlexandersOrderItem.php b/src/Entity/AlexandersOrderItem.php --- b/src/Entity/AlexandersOrderItem.php +++ b/src/Entity/AlexandersOrderItem.php @@ -34,42 +34,69 @@ */ class AlexandersOrderItem extends ContentEntityBase implements AlexandersOrderItemInterface { + /** + * {@inheritdoc} + */ public function getSku() { return $this->get('sku')->value; } + /** + * {@inheritdoc} + */ public function setSku($sku) { $this->set('sku', $sku); return $this; } + /** + * {@inheritdoc} + */ public function getQuantity() { return $this->get('quantity')->value; } + /** + * {@inheritdoc} + */ public function setQuantity($qty) { $this->set('quantity', $qty); return $this; } + /** + * {@inheritdoc} + */ public function getFile() { return $this->get('file')->value; } + /** + * {@inheritdoc} + */ public function setFile($url) { $this->set('file', $url); return $this; } + /** + * {@inheritdoc} + */ public function getAddFile() { return $this->get('foil')->value; } + /** + * {@inheritdoc} + */ public function setAddFile($url) { $this->set('foil', $url); return $this; } + /** + * {@inheritdoc} + */ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields = parent::baseFieldDefinitions($entity_type); @@ -84,12 +111,12 @@ $fields['file'] = BaseFieldDefinition::create('string') ->setLabel(t('File URL')) - ->setDescription(t('Location of file for Alexanders to print')) + ->setDescription(t('URL of file for Alexanders to print')) ->setRequired(TRUE); $fields['foil'] = BaseFieldDefinition::create('string') ->setLabel(t('Foil URL')) - ->setDescription(t('Location of foil for Alexanders to print')) + ->setDescription(t('URL of foil for Alexanders to print')) ->setRequired(TRUE); return $fields; diff -u b/src/Entity/AlexandersOrderItemInterface.php b/src/Entity/AlexandersOrderItemInterface.php --- b/src/Entity/AlexandersOrderItemInterface.php +++ b/src/Entity/AlexandersOrderItemInterface.php @@ -4,45 +4,82 @@ use Drupal\Core\Entity\ContentEntityInterface; +/** + * Interface AlexandersOrderItemInterface. + * + * @package Drupal\alexanders\Entity + */ interface AlexandersOrderItemInterface extends ContentEntityInterface { /** - * {@inheritdoc} + * Get SKU of product in order item. + * + * @return string + * Product stock keeping unit. */ public function getSku(); /** - * {@inheritdoc} + * Set SKU of order item. + * + * @param string $sku + * Stock keeping unit. + * + * @return $this */ public function setSku($sku); /** - * {@inheritdoc} + * Get QTY of order items printed by Alexanders. + * + * @return int + * # of items. */ public function getQuantity(); /** - * {@inheritdoc} + * Set number of times Alexanders should print this item. + * + * @param int $qty + * QTY of item that should be printed. + * + * @return $this */ public function setQuantity($qty); /** - * {@inheritdoc} + * Get primary file associated with item. + * + * @return string + * URL for the order item. */ public function getFile(); /** - * {@inheritdoc} + * Set primary file URL. + * + * @param string $url + * Public URL for the image/pdf file. + * + * @return $this */ public function setFile($url); /** - * {@inheritdoc} + * Get secondary file for item (guts or foil depending on product). + * + * @return string + * URL for the order item. */ public function getAddFile(); /** - * {@inheritdoc} + * Set secondary file URL. + * + * @param string $url + * Public URL for the image/pdf file. + * + * @return $this */ public function setAddFile($url); diff -u b/src/Entity/AlexandersOrderPhotobook.php b/src/Entity/AlexandersOrderPhotobook.php --- b/src/Entity/AlexandersOrderPhotobook.php +++ b/src/Entity/AlexandersOrderPhotobook.php @@ -2,14 +2,11 @@ namespace Drupal\alexanders\Entity; -use Drupal\Core\Entity\ContentEntityBase; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Field\BaseFieldDefinition; /** - * Defines the Alexanders Order entity. - * - * Mainly to a) remove commerce as a dependency and b) simplify management. + * Defines the Alexanders Photobook entity. * * @ContentEntityType( * id = "alexanders_order_photobook", @@ -33,35 +30,50 @@ */ class AlexandersOrderPhotobook extends AlexandersOrderItem { + /** + * {@inheritdoc} + */ public function getFile() { return $this->get('cover')->value; } + /** + * {@inheritdoc} + */ public function setFile($url) { $this->set('cover', $url); return $this; } + /** + * {@inheritdoc} + */ public function getAddFile() { return $this->get('guts')->value; } + /** + * {@inheritdoc} + */ public function setAddFile($url) { $this->set('guts', $url); return $this; } + /** + * {@inheritdoc} + */ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields = parent::baseFieldDefinitions($entity_type); $fields['cover'] = BaseFieldDefinition::create('string') - ->setLabel(t('File URL')) - ->setDescription(t('Location of file for Alexanders to print')) + ->setLabel(t('Cover URL')) + ->setDescription(t('URL of cover for Alexanders to print')) ->setRequired(TRUE); $fields['guts'] = BaseFieldDefinition::create('string') - ->setLabel(t('Foil URL')) - ->setDescription(t('Location of foil for Alexanders to print')) + ->setLabel(t('Guts URL')) + ->setDescription(t('URL of guts for Alexanders to print')) ->setRequired(TRUE); // Remove fields we don't need for this item. diff -u b/src/Entity/AlexandersShipment.php b/src/Entity/AlexandersShipment.php --- b/src/Entity/AlexandersShipment.php +++ b/src/Entity/AlexandersShipment.php @@ -2,7 +2,6 @@ namespace Drupal\alexanders\Entity; -use Drupal\Console\Utils\Create\Base; use Drupal\Core\Entity\ContentEntityBase; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Field\BaseFieldDefinition; @@ -34,51 +33,84 @@ */ class AlexandersShipment extends ContentEntityBase implements AlexandersShipmentInterface { + /** + * {@inheritdoc} + */ public function getMethod() { return $this->get('method')->value; } + /** + * {@inheritdoc} + */ public function setMethod($method) { $this->set('method', $method); return $this; } + /** + * {@inheritdoc} + */ public function getAddress() { return $this->get('address')->first(); } + /** + * {@inheritdoc} + */ public function setAddress($address) { $this->address = $address; return $this; } + /** + * {@inheritdoc} + */ public function getTracking() { return $this->get('tracking')->value; } + /** + * {@inheritdoc} + */ public function setTracking($number) { $this->set('tracking', $number); return $this; } + /** + * {@inheritdoc} + */ public function getTimestamp() { return $this->get('timestamp')->value; } + /** + * {@inheritdoc} + */ public function setTimestamp($time) { $this->set('timestamp', $time); return $this; } + /** + * {@inheritdoc} + */ public function getCost() { return $this->get('cost')->value; } + /** + * {@inheritdoc} + */ public function setCost($cost) { $this->set('cost', $cost); return $this; } + /** + * {@inheritdoc} + */ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields = parent::baseFieldDefinitions($entity_type); diff -u b/src/Entity/AlexandersShipmentInterface.php b/src/Entity/AlexandersShipmentInterface.php --- b/src/Entity/AlexandersShipmentInterface.php +++ b/src/Entity/AlexandersShipmentInterface.php @@ -4,26 +4,101 @@ use Drupal\Core\Entity\ContentEntityInterface; +/** + * Interface AlexandersShipmentInterface. + * + * @package Drupal\alexanders\Entity + */ interface AlexandersShipmentInterface extends ContentEntityInterface { + /** + * Get method of shipment. + * + * @return string + * Shipment method. + */ public function getMethod(); + /** + * Set shipping method string. + * + * @param string $method + * Shipment method for this order. + * + * @return $this + */ public function setMethod($method); + /** + * Get address for shipment. + * + * @return object + * Address object. + */ public function getAddress(); + /** + * Set address for this shipment. + * + * @param object $address + * Address object to associate w/ order. + * + * @return $this + */ public function setAddress($address); + /** + * Get tracking number for shipment, provided by Alexanders. + * + * @return string + * Tracking number. + */ public function getTracking(); + /** + * Set tracking number for this order. + * + * @param string $number + * Tracking number (or string) for order. + * + * @return $this + */ public function setTracking($number); + /** + * Get timestamp that order was shipped out. + * + * @return int + * Epoch timestamp for when order was shipped out. + */ public function getTimestamp(); + /** + * Set timestamp that order was shipped out. + * + * @param int $time + * Epoch timestamp. + * + * @return $this + */ public function setTimestamp($time); + /** + * Get cost of shipment. + * + * @return int + * Cost of shipment, / by 10 to get $ amount. + */ public function getCost(); + /** + * Set shipping method string. + * + * @param int $cost + * Cost of shipment. + * + * @return $this + */ public function setCost($cost); } diff -u b/tests/src/Functional/AlexanderApiSelf.php b/tests/src/Functional/AlexanderApiSelf.php --- b/tests/src/Functional/AlexanderApiSelf.php +++ b/tests/src/Functional/AlexanderApiSelf.php @@ -24,10 +24,9 @@ 'alexanders', 'system', 'datetime', - 'address' + 'address', ]; - /** @var \GuzzleHttp\Client */ private $client; private $url; @@ -52,13 +51,13 @@ 'quantity' => 1, 'file' => 'example.com', 'foil' => 'example.com', - ]) + ]), ], 'shipping' => [ AlexandersShipment::create([ 'method' => 'Test', 'address' => [], - ]) + ]), ], ])->save(); } @@ -86,2 +85,3 @@ } + } only in patch2: unchanged: --- a/README.md +++ b/README.md @@ -15,9 +15,9 @@ INTRODUCTION ------------ This module aims to integrate Alexander's printing API into Drupal so -customers of the website can order prints directly from them. It -currently relies on Drupal Commerce but the goal is to make the client -and API endpoints agnostic and remove the reliance on Drupal Commerce. +customers of the website can order prints directly from them. The module +itself does not really do anything and relies on third party integration, +opting to come with a simple pre-build Commerce module. See [the site API docs](https://app.swaggerhub.com/apis-docs/apa/apipostback.divvy.systems/1.0.0#/) @@ -28,10 +28,9 @@ for reference. REQUIREMENTS ------------ -Currently requires Drupal Commerce, this requirement should be removed -in the future. - -- https://drupal.org/project/commerce +Does not presently have any technical requirements, but does not do much on +it's own, so a custom module may be required. Come with simple Commerce +module with hooks to translate Commerce orders into Alexanders orders. INSTALLATION ------------ only in patch2: unchanged: --- a/alexanders.info.yml +++ b/alexanders.info.yml @@ -2,5 +2,4 @@ name: Alexander's Printing description: Managed interaction with the Alexander's printing API package: Custom type: module -version: 1.0 core: 8.x only in patch2: unchanged: --- a/src/Form/AlexandersManagementForm.php +++ b/src/Form/AlexandersManagementForm.php @@ -94,6 +94,17 @@ class AlexandersManagementForm extends ConfigFormBase { $config->save(); } + /** + * Generate API key with given prefix. + * + * @param string $prefix + * Prefix to append to API key. + * + * @return string + * Generated API key. + * + * @throws \Exception + */ private function apiKeyGenerator($prefix = 'alex') { $keyspace = 'abcdefghijklmnopqrstuvwxyz1234567890'; $str = $prefix . '-';