# Marketing redesign ## Premises and Uderstaning ### E-commerce - Shopping cart will be persistent, because admins need to able to access the customer cart in order to provide assistance if the customer requires. - For something to be listed on the e-commerce, it requires a product and a price table entry for that product variation to exist. - Also a product is only available on the e-commerce on the specified region if a price table entry exists for that product variation in that region. - Price tables are per product variation type and region. Products do not have direct relation to the price table entry. In other words, for a given product type and a given variation (e.g. scenic_projection_package and standard_license) all products of that type will have the same price. - Example: Annie JR will have the same price as Frozen JR since they are the same category (Scenic Projection JR). - Only partial content are shown to a non logged in user. - The front-end is responsible for quering only the wanted content, the back-end will only block critical things like shopping cart. ### CMS - Things that can have content - Homepage - Shows - Choreographer bio - Posts - Other pages on demand (will require code update to add the type on the enumerator). - Each content can have tags associated with them. - Attached files must be uploaded to the S3 using the existing upload endpoint on the Rails app and the file_id must be send on the POST/PUT request. - On updates (PUT), a diff will be executed and removed files will be removed from the association and marked for exclusion on the Rails filesystem. - On the Editor.js generated JSON, it should store the ids of the files to be able to later obtain the signed URL for that content. - On the editor.js content, it is required to use some kind of transformation function to obtain the signed url for the file. - On the front-end a caching system using may be implemented to store a map of file_id and a signed url and expiration date to prevent unnecessary consecutive call to the back-end to obtain the signed URLs. - Signed URLs for content will have 7 days of TTL (maximum allowed by AWS S3). - Tag management - Tags are managed, so each tag are stored on the database and have join tables for anything that is related to them. - In the future a tag management page can be added to the Backstage admin panel for admins to perform maintenance. ## Architectural design ### Overview ![](https://i.imgur.com/1WUpNC7.png) ### Database design https://dbdiagram.io/d/63f39e6f296d97641d82594d ### Non functional requirements - Class transformer package is used in all inbound and outbound request - For outbound it serializes data using the DTO. - Class validator package is used to validate BODY payload. - Every GET route that returns lists MUST have ``pagination``. - Pagination MUST be included as query parameters: - \?pagination\[page\]=\<integer\>&pagination\[pageSize\]=\<integer\> - page: Integer equal or greater than 1 - pageSize: Integer equal or greater than 1 - Under the hood those query parameters are converted to an JSON with the following structure and are part of the "query" json object: - ``PaginationOptions`` ```json { "pagination": { "page": "Integer", "pageSize": "Integer" } } ``` - Some GET routes MAY provide ``filters``. - Filters MUST be included under the filter query parameter: - ``?filter[<filterXName>]=<valueX>`` - ``<filterXName>``: string and must be available to filter by that property. - ``<valueX>``: string that will be automatically casted when needed to perform the filtering. - Also it can have ``$or`` and ``$groupBy`` operators. - When you add filters on the root, they are AND operations. But sometimes you need OR and also grouping. - ``$or?`` - A map of filters that will be used as OR operation. - ``$groupBy`` - Properties available to perform grouping. Note that if using $groupBy, the pagination will be done on the grouped properties and can be more resource intensive to process. - Under the hood those query parameters are converted to an JSON with the following structure: - ``Generic filter options`` ```json { "filters": { "filterXName": "valueX", "$or": { "filterXName": "valueX", }, "$groupBy": "propertyName" } } ``` - Some GET routes MAY provide ``includes``. - Including sub documents on the root document by a result of a JOIN operation MAY be supported for some routes and MUST be included as query parameters: - ``?includes[<property>][includes]=<boolean>&includes[<property>][filters][<filterAttribute>]=<value>`` - ``<property>``: the name of the related resource to include. - Includes: flag to include or not. Some endpoints MIGHT include by default some related data. - filters: map of filters to apply during the JOIN operation of the related resource - ``<filterAttribute>``: name of the supported filtering attribute for the JOIN and the desired value. - Under the hood those query parameters are converted to an JSON with the following structure: ```json { "includes": { "<property>": { "includes": "Boolean", "filters": { "<attribute>": "<value>" } } } } ``` - Responses must be REST compliant with the following: - NestJS is already configured to automaticaly handle most of the exceptions if correctly throwing then: - Database: Thown inside repositories. - ``EntityNotFoundException`` - Operations that requires manipulation of a resource and the resource is not found always throw this exception. - ``UniqueKeyViolationException`` - ``ForeignKeyViolationException`` - ``ConstraintViolationException`` - Business: Thrown inside services - **Work in progress...** - Programming: Thrown anytime manually by the developer when something is not right - ``IllegalArgumentException`` - ``SerializationException`` - Schema validations: Thrown by controllers or the ``PipeTransform``. - ``BadRequest`` - All exception will be handled by a exception filter and will format the message and return the appropriate code using one of the ``HttpException`` provided by NestJS. - Always create DTOs for payloads, responses and query parameters that can have be represented as json (e.g. ?pagination[page]). - It is required to decouple the API contract from the database model. - Protected routes - Some requests might required admin user or a specific role for a user. - Simple protection requires the user session to perform the operation. - Role protected routes require the user to be part of a role that allows the operation. - If simple protection is required and user is not informed. - ``UnauthorizedException`` - If role protection is required and user is not informed. - ``UnauthorizedException`` - If role protection is required and user does not have the necessary role. - ``ForbiddenException`` - POST - If it creates resources - Return 201 - Return the reference to the resource (the primary key (or compound) as JSON) - If it creates nothing - Return 204 - Void body - PUT - If success - Return 204 - Void body - If not found - ``NotFoundException`` - DELETE - If success - Return 204 - Void body - If not found - ``NotFoundException`` - If foreign key violation - ``ConflictException`` - GET - If it finds or if a search would result in an empty list. - 200 - Serialized resource as DTO or empty array. - If specific resource not found (i.e. obtained through URL params and not Query Params) - ``NotFoundException`` ## Modules ### Common - DTOs - ``ResourceIdentifier<T>`` ```json { "id": "T" } ``` - ``PaginatedResult<T>`` ```json { "page": "Integer", "pageSize": "Integer", "total": "Integer", "result": "Array<T>" } ``` - ``PaginatedGroupedResult<G, T>`` ```json { "page": "Integer", "pageSize": "Integer", "total": "Integer", "result": "Map<G, T>" } ``` ### Backstage This module just expose entities and resources from the Backstage. Its entities and repositories should be used only for READ operations. MODIFY and DELETE operations and schema modification are strictly forbidden to be done, these must be done at the Backstage API only. - DTOs - ``ScenicProjectionDto`` ```json { "id": "String", "name": "String", "approved": "Boolean", "fileSize": "Number" "public": "Boolean", "createdAt": "Date", "updatedAt": "Date" "show": "ShowDto" } ``` - ``ChoreoGuideDto`` ```json { "id": "String", "producer": "String", "otherCredits": "String", "licensor": "String", "marketingDescription": "String", "createdAt": "Date", "updatedAt": "Date", "show": "ShowDto", "choreographer": "ChoreographerDto" } ``` - ``SceneDto`` ```json { "id": "String", "name": "String", "position": "Number", "type": "String", "cueDescription": "String", "cueLine": "String", "cuePage": "String", "public": "Boolean", "featuredRank": "Number, "userStarsCount": "Number", "updatedAt": "Date", "act": "ScenicProjectionActDto" } ``` - ``DigitalDropDto extends SceneDto`` ```json { "tags": "Array<TagDto>", "themes": "Array<ThemeDto>", "product": "DigitalDropProductDto" } ``` - ``ProjectorDto`` ```json { "id": "String", "status": "Number", "unitNumber": "String", "manual": "String", "serialNumber": "String", "lensSerialNumber": "String", "thumbnailImage": "String", "createdAt": "Date", "updatedAt": "Date" } ``` #### Architechtural problems on Backstage - Database related: - Shows does not have a "variation" (i.e. type classifictaion - Junior, Regular) and it is not possible to directly search for a show of a given variation. Only the title of the show hints its classification. Due to that, one cannot search for choreo guides or digital drops that are related to the specific version for example: I want choreo guides for the Junior shows or digital drops for the junior shows. On the e-commerce there is a partial workaround with scenic projections becuase it's product requires a classification. ### Access Control (Auth) The access control module provides functionality to perform authentication and authorization. Authentication is done at the Backstage service directly by the front-end or other client, which returns a JWT token (Bearer Auth). On the NestJS side, each endpoint that requires authentication must enforce by appling the AuthGuard at controller or route level and requires the JWT token to be set on the Authorization header on each request. Internally the Access Control module will validate the token against the Backstage service, fetch the user roles, create the session object, attach the session to the request and cache the session on memory. We cache on memory because we do not have a proper Redis configured and the cache can be configured by environment variables. Normally only POST, PUT and DELETE are protected enpoints and only require the user to be a Staff. For that, besides using ``AuthGuard`` we need to add the ``StaffGuard`` to the enpoint or controller. Some enpoints might require additional protection, such as for the Price Table endpoints. In that scenarios, the user needs to be attached to a role that has a Permit that allows the action. Such requirement can be added to the route by using the ``RequirePermit`` decorator. For examples check the Price Table sub feature of e-commerce. Required environment variables: | Variable | Type and constraint | Description | |-----------------|-------------------------|-------------------------------------------------------| | AUTH_URL | ``string`` | URL to Backstage API to perform authentication. | | AUTH_CACHE_TTL | ``number`` >= 60 | Time to live in seconds for the Backstage auth cache. | | AUTH_CACHE_SIZE | ``number`` > 0 | Size of the auth cache in number of cached users. | - Enums - ``AccountTypeEnum`` - ``CLIENT`` - ``STAFF`` - ``ActionsEnum`` - ``READ`` - ``MODIFY`` - ``DELETE`` - Models - ``Resource`` ```json { "name": "String", "actions": "Set<ActionsEnum>" } ``` - ``RequiredPermit`` ```json { "resource": "Resource", "action": "ActionsEnum"; } ``` - ``Permit`` ```json { "resource": "String", "actions": "ActionsEnum" } ``` ### E-commerce #### Common - Enums - Note: All variations are represented as enumerators. In a normal e-commerce this is not ideal and a dedicated resource on the API is used to manage those, but due to the specific nature of the BMD business and time constraints, enumerators were the best choice at the moment. - ``ProductTypeEnum`` - ``SCENIC_PROJECTION_PACKAGE`` - ``CHOREO_GUIDE`` - ``DIGITAL_DROP`` - ``PROJECTOR`` - ``ACCESSORY`` - ``ShowVariationEnum`` - ``JUNIOR`` - ``REGULAR`` - ``ProjectorVariationEnum`` - ``ECONOMY_LITE`` - ``ECONOMY`` - ``BASIC`` - ``STANDARD`` - ``PLUS`` - ``PREMIUM`` - ``AccessoryVariationEnum`` - ``EXTENSION_50`` - ``EXTENSION_200`` - ``COMMON_MOUNT`` - ``HEAVY_DUTY_MOUNT`` #### Regions The regions determines where the e-commerce products are available for renting/purchase. Other parts of the world can still view visit the site, but only descriptions will be shown. Disabling a region will result in the e-commerce to not become unavailable on that region. It’s the front-end responsibility to call the back-end to identify the region and with that information perform requests for the specified region. ##### Maintenance Only admin users with the role "pricer" are allowed to perform POST, PUT and DELETE operations on this resource. ##### DTOs - ``UpsertRegionDto`` ```json { "name": "String (unique, maxLength=250, minLength=3)", "enabled": "Boolean", "currency": { "name": "String (maxLength=250, , minLength=3)", "symbol": "String (maxLength=5, , minLength=1)" } } ``` - ``RegionDto`` ```json { "id": "String", "name": "String", "enabled": "Boolean", "currency": { "name": "String", "symbol": "String" } } ``` ##### Endpoints - ``POST /ecommerce/regions`` - Description: Adds a new e-commerce region availability. - Protected: ``Staff`` - Body: ``UpsertRegionDto`` - Return - Sucess - Code: 201 - Body: ``ResourceIdentifierDto<Integer>`` - Exception - ``400 - Bad Request``: Validation related. - ``409 - Conflict``: A region with the specified name already exists. - ``PUT /ecommerce/regions/:id`` - Description: Replaces a region updating it. - Protected: ``Staff`` - URL parameters - ``id: Integer`` - The Id of the region to replace. - Body: ``UpsertRegionDto`` - Return - Success - Code: 204 - Body: ``Void`` - Exception - ``400 - Bad Request``: Validation related. - ``409 - Conflict``: A region with the specified name already exists. - ``DELETE /ecommerce/regions/:id`` - Description: Deletes the region by the specified id. - Protected: ``Staff`` - URL parameters - ``id: Integer`` - The id of the region to be deleted. - Return - Success: - Code: 204 - Body: ``Void`` - Exception: - ``409 - Conflict``: This resource has associations, please delete them before. - ``GET /ecommerce/regions`` - Description: Gets the paginated list of regions. - Protected: ``Public`` - Query parameters: - ``filter?`` - ``enabled?: Boolean`` - Return - Success - Code: 200 - Body: ``PaginatedResultDto<RegionDto>`` - Exception - ``HttpNotFoundException``: No results returned from the query. - ``GET /ecommerce/regions/:id`` - Description: Gets a region by its id. - Protected: ``Public`` - URL parameters - ``id: Integer`` - The id of the region to fetch. - Return - Success - Code: 200 - Body: ``RegionDto`` - Exception - ``404 - Not Found``: No results returned from the query. #### Price Tables Price tables are used to price products. The pricing is done on the product type and product variation by region and are not specific per individual product, in other words, the pricing is done by category. For a specific product variation to be listed on the e-commerce for a specific region, its category and region must be an entry on this resource. Only one enabled price entry for a product, variation and region can be enabled at a time. Also, some products are licensed (i.e. Licensor, e.g. MTI), and the billing is not done by BMD and normally the customer needs to input an access code for that content and billing is done through the licensor. To take that into account, each product has a licensor and the licensor can also be BMD, but licensors where the payment is not processed by us, the ``charge`` flag is marked as false. With all that complexity in pricing, the shopping cart and checkout process will have a specific service for calculating the total order and may use the Strategy design pattern. See the shopping cart and checkout resources for more information. ##### Maintenance Only admin users with the "pricer" (Permit: ecommerce.priceTables, [READ, MODIFY, DELTE) role are allowed to perform POST, PUT and DELETE operations on this resource. A page with list and insert/update is added to the Backstage admin panel for the "pricer" to be able to add and update prices. ##### DTOs - ``abstract UpsertPriceTableDto`` ```json { "enabled": "Boolean", "type": "ProductTypeEnum", "price": "String (regex decimal[n, 0-2])", "tax": "String (regex decimal[n, 0-2])", "regionId": "Integer" } ``` - ``UpsertScenicProjectionPackagePriceTableDto extends UpsertPriceTableDto`` ```json { "variation": "ScenicProjectionPackageVariationEnum", "priceProLicense": "String (regex decimal[n, 0-2])", "taxProLicense": "String (regex decimal[n, 0-2])", "priceExtraWeek": "String (regex decimal[n, 0-2])", "taxExtraWeek": "String (regex decimal[n, 0-2])", "rentWeeks": "Integer (n > 0)" } ``` - ``UpsertChoreoGuidePriceTableDto extends UpsertPriceTableDto`` ```json { "variation": "ChoreoGuideVariationEnum" } ``` - ``UpsertDigitalDropPriceTableDto extends UpsertPriceTableDto`` ```json { "variation": "DigitalDropVariationEnum" } ``` - ``UpsertProjectorPriceTableDto extends UpsertPriceTableDto`` ```json { "variation": "ProjectorVariationEnum" } ``` - ``abstract PriceTableDto`` ```json { "id": "Integer" "enabled": "Boolean", "type": "ProductTypeEnum", "price": "String (regex decimal[n, 0-2])", "tax": "String (regex decimal[n, 0-2])", "regionId": "Integer" } ``` - ``ScenicProjectionPackagePriceTableDto extends PriceTableDto`` ```json { "variation": "ScenicProjectionPackageVariationEnum", "priceProLicense": "String (regex decimal[n, 0-2])", "taxProLicense": "String (regex decimal[n, 0-2])", "priceExtraWeek": "String (regex decimal[n, 0-2])", "taxExtraWeek": "String (regex decimal[n, 0-2])", "rentWeeks": "Integer (n > 0)" } ``` - ``ChoreoGuidePriceTableDto extends PriceTableDto`` ```json { "variation": "ChoreoGuideVariationEnum" } ``` - ``ProjectorPriceTableDto extends PriceTableDto`` ```json { "variation": "ProjectorVariationEnum" } ``` ##### Endpoints - ``POST /ecommerce/price-tables`` - Description: Creates a new price entry for a product variation on a region. - Protected: ``Staff->ecommerce.priceTable->MODIFY`` - Body: ``UpsertPriceTableDto`` - Return - Success - Code: 201 - Body: ``ResourceIdentifierDto<Integer>`` - Exception - ``ConflictException``: Another price table entry for this product variation in this region is already enabled. Please disable the old one before adding this one. - ``PUT /ecommerce/price-tables/:id`` - Description: Replaces a price table entry, updating it. - Protected: ``Staff->ecommerce.priceTable->MODIFY`` - URL parameters - ``id: Integer`` - The id of the price table entries - Body: ``UpsertPriceTableDto`` - Return - Success - Code: 204 - Body: ``Void`` - Exception - ``NotFound``: The price table was not found. - ``ConflictException``: Another price table entry for this product variation in this region is already enabled. Please disable the old one before adding this one. - ``DELETE /ecommerce/price-tables/:id`` - Description: Deletes the price table entry by the specified id. - Protected: ``Staff->ecommerce.priceTable->DELETE`` - URL parameters - ``id: Integer`` - The id of the price table entry to be deleted. - Return - Success: - Code: 204 - Body: ``Void`` - Exception - ``NotFound``: The price table was not found. - ``GET /ecommerce/price-tables`` - Description: Gets the paginated list of price table entries. - Protected: ``Public`` - Query parameters: - ``pagination: PaginationOptions`` - ``filter?`` - ``enabled?: Boolean `` - ``type?: ProductTypeEnum`` - ``variation?: ScenicProjectionPackageVariationEnum | ChoreograpyGuideVariationEnum | DigitalDropVariationEnum | ProjectorVariationEnum | AccessoryVariationEnum`` - ``regionId?: Integer`` - ``includes?`` - ``region?`` - ``includes: boolean`` - Return - Success - Code: 200 - Body: ``PaginatedResultDto<PriceTableDto>`` - Exception - ``HttpNotFoundException``: No results returned from the query. - ``GET /ecommerce/price-tables/:id`` - Description: Gets a price table entry by its id. - Protected: ``Public`` - URL parameters - ``id: Integer`` - The id of the price table entry to fetch. - Query parameters: - ``includes?`` - ``region?`` - ``includes: boolean`` - Return - Success - Code: 200 - Body: ``PriceTableDto`` - Exception - ``NotFoundException``: Price table not found. - ``GET /ecommerce/price-tables/:regionId/:type/:variation`` - Description: Retrieves the current price for a product variation in a region. - Protected: ``Public`` - URL parameters - ``regionId: Integer`` - The id of region. - ``type: ProductTypeEum`` - The product type. - ``variation: ScenicProjectionPackageVariationEnum | ChoreograpyGuideVariationEnum | ProjectorVariationEnum | AccessoryVariationEnum`` - The product variation. - Query parameters: - ``includes?`` - ``region?`` - ``includes: boolean`` - Return - Success - Code: 200 - Body: ``PriceTableDto`` - Exception - ``NotFoundException``: Price table not found. #### Products Products are the things that are listed on the e-commerce. If a rentable (e.g. digital drop) is created but a product is not, it will not be listed on the e-commerce. Also for a product to be listed on the e-commerce on a specific region, a price entry must exist for this product on that region and be enabled. Only one product variation can exist for an individual product (e.g. Scenic Projection Package Frozen can only have one product for REGULAR and one for JUNIOR). Note that the Junior version is normally a separate package, which technically allow a junior version to be registered as REGULAR or PRO on the products, which is wrong and must be handled by Staff during product registration. This happens due to the current data organization and needs to be changed in the future. Products can be enabled or disabled, thus disabling the availability in all regions. But if no pricing is defined for a product variation for a given region, it will not be available on the e-commerce for that region. When a PROJECTOR is added to the shopping cart, shipping configuration will be added automatically to the shopping cart as well. Check shopping cart resource for more information. ###### ###### DTOs - ```abstract UpsertProductDto``` ```json { "enabled": "Boolean", "type": "ProductTypeEnum" } ``` - ``UpsertScenicProjectionProductDto extends UpsertProductDto`` ```json { "variation": "ScenicProjectionPackageVariationEnum", "scenicProjectionId": "String" } ``` - ``UpsertChoreoGuideProductDto extends UpsertProductDto`` ```json { "variation": "ChoreograpyGuideVariationEnum", "choreoGuideId": "String" } ``` - ``UpsertDigitalDropProductDto extends UpsertProductDto`` ```json { "variation": "DigitalDropVariationEnum", "digitalDropId": "String" } ``` - ``UpsertProjectorProductDto extends UpsertProductDto`` ```json { "variation": "ProjectorVariationEnum", "projectorId": "String" } ``` - ``abstract ProductDto`` ```json { "id": "Integer" "enabled": "Boolean", "type": "ProductTypeEnum" } ``` - ``ScenicProjectionProductDto extends ProductDto`` ```json { "variation": "ScenicProjectionVariationEnum", "scenicProjection": "ScenicProjectionDto" } ``` - Note that "scenicProjection" may only inlcude the "id" in some scenarions. Additionaly properties might be included depending on the "includes" query configuration. - ``ChoreoGuideProductDto extends ProductDto`` ```json { "variation": "ChoreograpyGuideVariationEnum", "choreoGuide": "ChoreoGuideProductDto" } ``` - Note that "choreoGuide" may only inlcude the "id" in some scenarions. Additionaly properties might be included depending on the "includes" query configuration. - ``DigitalDropProductDto extends ProductDto`` ```json { "productVariation": "DigitalDropVariationEnum", "digitalDrop": "DigitalDropDto" } ``` - Note that "digitalDrop" may only inlcude the "id" in some scenarions. Additionaly properties might be included depending on the "includes" query configuration. - ``ProjectorProductDto extends ProductDto`` ```json { "productVariation": "ProjectorVariationEnum", "projector": "ProjectorDto" } ``` - Note that "projector" may only inlcude the "id" in some scenarions. Additionaly properties might be included depending on the "includes" query configuration. ###### Endpoints - ``POST /ecommerce/products`` - Description: Creates a new product. - Protected: ``Staff`` - Body: ``UpsertScenicProjectionProductDto | UpsertChoreoGuideProductDto | UpsertDigitalDropProductDto | UpsertProjectorProductDto`` - Return - Success - Code: 201 - Body: ``ResourceIdentifierDto<Integer>`` - Exception - ``ConflictException``: This product variation already exists. - ``PUT /ecommerce/products/:id`` - Description: Replaces a product, updating it. - Protected: ``Staff`` - URL parameters - ``id: Integer`` - The id of the product to replace. - Body: ``UpsertScenicProjectionProductDto | UpsertChoreoGuideProductDto | UpsertDigitalDropProductDto | UpsertProjectorProductDto`` - Return - Success - Code: 204 - Body: ``Void`` - Exception - ConflictException: This product variation already exists. - ``DELETE /ecommerce/products/:id`` - Description: Deletes a product by the specified id. - Protected: ``Staff`` - URL parameters - ``id: Integer`` - The id of the product to be deleted. - Return - Success: - Code: 204 - Body: ``Void`` - Exception: - ``ConflictException``: This resource has associations and cannot be deleted. - ``GET /ecommerce/products`` - Description: Gets a paginated list of products - Protected: ``Public`` - Query parameters - ``filter?`` - ``enabled?: boolean`` - ``type?: ProductTypeEnum`` - ``variation?: ScenicProjectionVariationEnum | ChoreoGuideVariationEnum | ProjectorVariationEnum`` - This will be validated depending on the "type" and must only be used alongside with "type". - ``licensor: number`` - The id of the licensor - ``includes?`` - ``description?: {includes: boolean}`` - Includes the details of the associated resource (e.g. Digital Drop). - Return - Success - Code: 200 - Body: ``PaginatedResultDto<ProductDto>`` - ``GET /ecommerce/products/:id`` - Description: Gets a product by its id. - Protected: ``Public`` - URL parameters - ``id: Integer`` - The id of the product to fetch. - Return - Success - Code: 200 - Body: ``ProductDto`` - Exception - ``NotFoundException``: No results returned from the query. - ``GET /ecommerce/products/search/:regionId/global`` - Description: For a given region, perform a global search on available products of types: Choreography Guides and Scenic Projection Packages. - Protected: ``Public`` - URL parameters - ``regionId: number`` - The id of the desired region to obtain products. What determines the availability of products is the existence of a pricing table for that product on that region. - Query Parameters: - ``searchText: string`` - Searches by show title - ``licensors: Array<number>`` - The ids of the licensors. - ``versions: Array<'JUNIOR'|'REGULAR'>`` - Searchs only shows of a specif type. Note that due to a limitation on backstage, this search is only going to work when the show has a related Scenic Projection Package. - ``resources: Array<'SCENIC_PROJECTION_PACKAGE'|'CHOREO_GUIDE'> - Return - Success - Code: 200 - Body: ``PaginatedResultDto<ShowDto>`` - ``GET /ecommerce/products/search/:regionId/scenic-projections`` - Description: For a given region, perform a search on available Scenic Projections. - Protected: ``Public`` - URL parameters - ``regionId: number`` - The id of the desired region to obtain products. What determines the availability of products is the existence of a pricing table for that product on that region. - Query Parameters: - ``searchText: string`` - Searches by show title - ``licensors: Array<number>`` - The ids of the licensors. - ``versions: Array<'JUNIOR'|'REGULAR'>`` - Searchs only shows of a specif type. Note that due to a limitation on backstage, this search is only going to work when the show has a related Scenic Projection Package. - Return - Success - Code: 200 - Body: ``PaginatedResultDto<ShowDto>`` - ``GET /ecommerce/products/search/:regionId/choreo-guides`` - Description: For a given region, perform a search on available Choreography Guides. - Protected: ``Public`` - URL parameters - ``regionId: number`` - The id of the desired region to obtain products. What determines the availability of products is the existence of a pricing table for that product on that region. - Query Parameters: - ``searchText: string`` - Searches by show title - ``licensors: Array<number>`` - The ids of the licensors. - ``versions: Array<'STANDARD'>`` - Searchs only shows of a specif type. Note that due to a limitation on backstage, this search is only going to work when the show has a related Scenic Projection Package. - Return - Success - Code: 200 - Body: ``PaginatedResultDto<ShowDto>`` - ``GET /ecommerce/products/search/:regionId/digital-drops`` - Description: For a given region, perform a search on available Choreography Guides. Internally the digital drop Show is treated as Theme and the Theme is internally treated as Tag. The filters are operated as AND conditions to allow customers to narrow down specific scenarios. Only the things that are arrays are treated as OR inside that group, example: given searchText, styles and settings, internally a query like the following will execute: ``(drop.name LIKE '%searchText%' OR show.name LIKE '%searchText%' OR theme.name LIKE '%searchText%') AND (theme.name IN settings) AND (theme.name IN styles)``. - Protected: ``Public`` - URL parameters - ``regionId: number`` - The id of the desired region to obtain products. What determines the availability of products is the existence of a pricing table for that product on that region. - Query Parameters: - ``filters?`` - When not using group by: - ``searchText: string`` - This searches by Drop name, Show name and Ad Hoc tags. - ``settings: Array<string>`` - The settings of the digital drop, e.g. Interiors, Exteriors, Night, etc. - ``styles: Array<string>`` - The style of the digital drop, e.g. Abstract, Cartoon, etc. - When using group by: - ``searchText: string`` - This searches only by the Show or Theme name. - ``groupBy?: 'show'|'theme'`` - Return - Success - Code: 200 - Body: - If not grouped: ``PaginatedResultDto<DigitalDropDto>`` - If group by 'show': ``PaginatedResultDto<ThemeDto>`` - If group by 'theme': ``PaginatedResultDto<TagDto>`` - ``GET /search/:regionId/digital-drops/show/:showId`` - Description: For a given region, perform a search on available Digital Drops that are associated with a specific show (internally the used term is Theme). - Protected: ``Public`` - URL parameters - ``regionId: number`` - The id of the desired region to obtain products. What determines the availability of products is the existence of a pricing table for that product on that region. - ``showId: string`` - The show to search digital drops. - Return - Success - Code: 200 - Body: ``PaginatedResultDto<DigitalDropDto>`` - ``GET /search/:regionId/digital-drops/theme/:themeId`` - Description: For a given region, perform a search on available Digital Drops that are associated with a specific theme (internally the used term is Tag). - Protected: ``Public`` - URL parameters - ``regionId: number`` - The id of the desired region to obtain products. What determines the availability of products is the existence of a pricing table for that product on that region. - ``themeId: string`` - The theme to search digital drops. - Return - Success - Code: 200 - Body: ``PaginatedResultDto<DigitalDropDto>`` - ### CMS #### Tags Tags are one way to sort content and improve organization and search experience for both the customer and the administrators. Each tag are unique and can be realated to basically anything, while allowing easy access and maintenance. Also tags can be disabled to prevent usage if not wanted and will not be considered on searchs. Right now tags will be supported only for ``content``, meaning that any content can have 0 to n tags associated. Please refer to section Premises and Uderstaning - CMS to review what a content is. ##### Maintenance Any admin can perform POST, PUT and DELETE operations on this resource. ##### DTOs - ``UpsertTagDto`` ```json { "name": "String (unique)", "enabled": "boolean" } ``` - ``TagDto`` ```json { "id": "Integer", "name": "String", "enabled": "boolean" } ``` ##### Endpoints - ``POST /cms/tags`` - Descripiton: Creates a new tag. - Body: ``UpsertTagDto`` - Return - Success - Code: 201 - Body ``ResourceIdentifierDto<Integer>`` - Exception - ``HttpBadRequestException``: If another tag with the same name already exists. - ``PUT /cms/tags/:id`` - Descripiton: Replaces an existing tag, updating it. - URL Parameters - ``id: Integer`` - The id of the tag to be replaced. - Body: ``UpsertTagDto`` - Return - Success: - Code: 204 - Body: ``Void`` - Exception: - ``HttpBadRequestException``: If another tag with the same name already exists. - ``DELETE /cms/tags/:id`` - Descripiton: Deletes a tag with the specified id. - URL Parameters - ``id: Integer`` - The id of the tag to be deleted - Return - Success: - Code: 204 - Body: ``Void`` - Exception: - ``HttpBadRequestException``: If the tag has relations and thus cannot be excluded. - ``GET /cms/tags`` - Descripiton: Gets a paginated list of tags - Query parameters - ``filter?`` - ``name: String`` - ``enabled: Boolean`` - Return - Sucess - Code: 200 - Body: ``PaginatedResult<TagDto> - Exception - ``HttpNotFoundException``: No results for the given query. - ``GET /cms/tags/:id`` - Descripiton: Gets a paginated list of tags - Query parameters - ``filter?`` - ``name: String`` - ``enabled: Boolean`` - Return - Sucess - Code: 200 - Body: ``PaginatedResult<TagDto> - Exception - ``HttpNotFoundException``: No results for the given query. #### Shopping Carts #### Contents Content is the core of a CMS, it allows admins to create custom page description for products, modify content on many parts of marketing site (e.g. home, about, etc.) and also engage with customers and leads by having a blog and the ability to link blog posts to the e-commerce products. A content is simply a piece of rich text which its structure is irrelevant to the back-end or business logic, but can be linked to resources. Technically content is classified by type and with the type some constraints and relations can be applied. For example, a content of type ``show`` can only be applied to a ``regular`` or ``pro`` show, while ``show_jr`` can be applied to shows of ``junior`` type. If a content is of type ``post``, for example, it can be linked to any type of product or none. ##### Maintenance Any admin user can perform POST, PUT and DELETE on this resource. ##### Enums - ``ContentTypeEnum`` - ``SHOW`` - ``SHOW_JR`` - ``CHOREO_GUIDE`` - ``CHOREO_BIO`` - ``DIGITAL_DROP`` - ``POST`` - ``HOMEPAGE`` - ``ABOUT_US`` - ``LEGAL``