diff --git a/smithy/model/article/article-apis.smithy b/smithy/model/article/article-apis.smithy new file mode 100644 index 0000000..a21b3a9 --- /dev/null +++ b/smithy/model/article/article-apis.smithy @@ -0,0 +1,78 @@ +$version: "2" + +namespace shopping.inandout.article + +use shopping.inandout#DeleteRestrictedError +use shopping.inandout#InternalServerError +use shopping.inandout#InvalidInputError +use shopping.inandout#PositiveDouble +use shopping.inandout#ResourceAlreadyExistsError +use shopping.inandout#ResourceNotFoundError +use shopping.inandout#UUID +use shopping.inandout.product#ProductSummary + +resource Article { + identifiers: { + articleId: UUID + } + properties: { + productSummary: ProductSummary + brandId: UUID + price: PositiveDouble + currency: String + createdAt: Timestamp + updatedAt: Timestamp + } + create: CreateArticle + read: GetArticle + update: UpdateArticle + delete: DeleteArticle +} + +@http(method: "POST", uri: "/brands/{brandId}/articles") +operation CreateArticle { + input: CreateArticleInput + output: CreateArticleOutput + errors: [ + InvalidInputError + ResourceAlreadyExistsError + InternalServerError + ] +} + +@readonly +@http(method: "GET", uri: "/brands/{brandId}/articles/{articleId}") +operation GetArticle { + input: GetArticleInput + output: GetArticleOutput + errors: [ + InvalidInputError + ResourceNotFoundError + InternalServerError + ] +} + +@http(method: "PATCH", uri: "/brands/{brandId}/articles/{articleId}") +operation UpdateArticle { + input: UpdateArticleInput + output: UpdateArticleOutput + errors: [ + InvalidInputError + ResourceNotFoundError + InternalServerError + ] +} + +@idempotent +@http(method: "DELETE", uri: "/brands/{brandId}/articles/{articleId}") +@documentation("Restricted cascading operation, references for stands should NOT exist") +operation DeleteArticle { + input: DeleteArticleInput + output: DeleteArticleOutput + errors: [ + InvalidInputError + ResourceNotFoundError + DeleteRestrictedError + InternalServerError + ] +} diff --git a/smithy/model/article/article-io.smithy b/smithy/model/article/article-io.smithy new file mode 100644 index 0000000..b45b8a2 --- /dev/null +++ b/smithy/model/article/article-io.smithy @@ -0,0 +1,60 @@ +$version: "2" + +namespace shopping.inandout.article + +use shopping.inandout#BrandIdMixin +use shopping.inandout#PositiveDouble +use shopping.inandout#UUID +use shopping.inandout.product#CreateProductInput +use shopping.inandout.product#Product + +@references([ + { + resource: Product + } +]) +structure CreateArticleInput with [BrandIdMixin, ArticleInputMixin] { + @required + price: PositiveDouble + + // Clients must choose between providing a product id/details. + // Not none, not both, one field must be filled. + @notProperty + @documentation("Existing product referenced in a new article") + productId: UUID + + @notProperty + @documentation("Creates a new product") + createProductInput: CreateProductInput +} + +structure CreateArticleOutput { + @required + articleId: UUID +} + +structure GetArticleInput with [BrandIdMixin] { + @required + @httpLabel + articleId: UUID +} + +structure GetArticleOutput with [ArticleOutputMixin] {} + +structure UpdateArticleInput with [BrandIdMixin, ArticleInputMixin] { + @required + @httpLabel + articleId: UUID + + price: PositiveDouble +} + +structure UpdateArticleOutput with [ArticleOutputMixin] {} + +structure DeleteArticleInput with [BrandIdMixin] { + @required + @httpLabel + articleId: UUID +} + +structure DeleteArticleOutput {} diff --git a/smithy/model/article/article-types.smithy b/smithy/model/article/article-types.smithy new file mode 100644 index 0000000..2d01a1d --- /dev/null +++ b/smithy/model/article/article-types.smithy @@ -0,0 +1,42 @@ +$version: "2" + +namespace shopping.inandout.article + +use shopping.inandout#AuditMetadata +use shopping.inandout#PositiveDouble +use shopping.inandout#UUID +use shopping.inandout.brand#Brand +use shopping.inandout.product#ProductSummary + +@mixin +structure ArticleMixin { + currency: String +} + +@mixin +structure ArticleInputMixin with [ArticleMixin] {} + +@mixin +@references([ + { + resource: Brand + } + { + resource: Article + } +]) +structure ArticleOutputMixin with [AuditMetadata, ArticleMixin] { + @required + brandId: UUID + + @required + articleId: UUID + + @required + productSummary: ProductSummary + + @required + price: PositiveDouble +} + +structure ArticleSummary with [ArticleOutputMixin] {} diff --git a/smithy/model/brand/brand-apis.smithy b/smithy/model/brand/brand-apis.smithy index 749ad33..18a68bb 100644 --- a/smithy/model/brand/brand-apis.smithy +++ b/smithy/model/brand/brand-apis.smithy @@ -1,15 +1,15 @@ $version: "2" -namespace com.shopping.inandout.brand +namespace shopping.inandout.brand -use com.shopping.inandout#DeleteRestrictedError -use com.shopping.inandout#ImageUrl -use com.shopping.inandout#InternalServerError -use com.shopping.inandout#InvalidInputError -use com.shopping.inandout#ResourceAlreadyExistsError -use com.shopping.inandout#ResourceName -use com.shopping.inandout#ResourceNotFoundError -use com.shopping.inandout#UUID +use shopping.inandout#DeleteRestrictedError +use shopping.inandout#ImageUrl +use shopping.inandout#InternalServerError +use shopping.inandout#InvalidInputError +use shopping.inandout#ResourceAlreadyExistsError +use shopping.inandout#ResourceName +use shopping.inandout#ResourceNotFoundError +use shopping.inandout#UUID resource Brand { identifiers: { @@ -30,7 +30,7 @@ resource Brand { @http(method: "POST", uri: "/brands") operation CreateBrand { input: CreateBrandInput - output: BrandSummary + output: CreateBrandOutput errors: [ InvalidInputError ResourceAlreadyExistsError @@ -42,7 +42,7 @@ operation CreateBrand { @http(method: "GET", uri: "/brands/{brandId}") operation GetBrand { input: GetBrandInput - output: BrandSummary + output: GetBrandOutput errors: [ InvalidInputError ResourceNotFoundError @@ -53,7 +53,7 @@ operation GetBrand { @http(method: "PATCH", uri: "/brands/{brandId}") operation UpdateBrand { input: UpdateBrandInput - output: BrandSummary + output: UpdateBrandOutput errors: [ InvalidInputError ResourceNotFoundError @@ -66,7 +66,7 @@ operation UpdateBrand { @documentation("Restricted cascading operation, references for stores and articles should NOT exist") operation DeleteBrand { input: DeleteBrandInput - output: BrandSummary + output: DeleteBrandOutput errors: [ InvalidInputError ResourceNotFoundError diff --git a/smithy/model/brand/brand-io.smithy b/smithy/model/brand/brand-io.smithy index 1e77ef9..cc39439 100644 --- a/smithy/model/brand/brand-io.smithy +++ b/smithy/model/brand/brand-io.smithy @@ -1,16 +1,18 @@ $version: "2" -namespace com.shopping.inandout.brand +namespace shopping.inandout.brand -use com.shopping.inandout#ImageUrl -use com.shopping.inandout#ResourceName -use com.shopping.inandout#UUID +use shopping.inandout#ResourceName +use shopping.inandout#UUID -structure CreateBrandInput { +structure CreateBrandInput with [BrandInputMixin] { @required name: ResourceName +} - logoUrl: ImageUrl +structure CreateBrandOutput { + @required + brandId: UUID } structure GetBrandInput { @@ -19,18 +21,22 @@ structure GetBrandInput { brandId: UUID } -structure UpdateBrandInput { +structure GetBrandOutput with [BrandOutputMixin] {} + +structure UpdateBrandInput with [BrandInputMixin] { @required @httpLabel brandId: UUID name: ResourceName - - logoUrl: ImageUrl } +structure UpdateBrandOutput with [BrandOutputMixin] {} + structure DeleteBrandInput { @required @httpLabel brandId: UUID } + +structure DeleteBrandOutput {} diff --git a/smithy/model/brand/brand-types.smithy b/smithy/model/brand/brand-types.smithy index 04f8cbc..51c17ca 100644 --- a/smithy/model/brand/brand-types.smithy +++ b/smithy/model/brand/brand-types.smithy @@ -1,23 +1,32 @@ $version: "2" -namespace com.shopping.inandout.brand +namespace shopping.inandout.brand -use com.shopping.inandout#ImageUrl -use com.shopping.inandout#ResourceName -use com.shopping.inandout#UUID - -structure BrandSummary { - @required - brandId: UUID - - @required - name: ResourceName +use shopping.inandout#AuditMetadata +use shopping.inandout#ImageUrl +use shopping.inandout#ResourceName +use shopping.inandout#UUID +@mixin +structure BrandMixin { logoUrl: ImageUrl +} + +@mixin +structure BrandInputMixin with [BrandMixin] {} +@mixin +@references([ + { + resource: Brand + } +]) +structure BrandOutputMixin with [AuditMetadata, BrandMixin] { @required - createdAt: Timestamp + brandId: UUID @required - updatedAt: Timestamp + name: ResourceName } + +structure BrandSummary with [BrandOutputMixin] {} diff --git a/smithy/model/common.smithy b/smithy/model/common.smithy index 34d0b92..9334b0c 100644 --- a/smithy/model/common.smithy +++ b/smithy/model/common.smithy @@ -17,3 +17,100 @@ string ResourceName @pattern("^https?://[a-zA-Z0-9\\-._~:/?#\\[\\]@!$&'()*+,;=%]+\\.(jpg|jpeg|png|gif)$") @length(min: 8, max: 255) string ImageUrl + +@pattern("^[a-zA-Z0-9\\-, ]+$") +@length(min: 8, max: 255) +string Description + +// All UTC offsets fall between this interval: [-12, 14]. +// See: https://en.wikipedia.org/wiki/List_of_UTC_offsets. +@range(min: -12, max: 14) +integer UTCTimezone + +@range(min: 0, max: 59) +integer Minute + +@range(min: 0, max: 23) +integer Hour + +@range(min: -180, max: 180) +double Longitude + +@range(min: -90, max: 90) +double Latitude + +@range(min: 0, max: 100) +double Percentage + +@range(min: 0) +integer NaturalNumber + +@range(min: 0) +double PositiveDouble + +enum DayType { + MON = "MON" + TUE = "TUE" + WED = "WED" + THU = "THU" + FRI = "FRI" + SAT = "SAT" + SUN = "SUN" +} + +structure Time { + @required + hour: Hour + + @required + minute: Minute +} + +structure TimeRange { + @required + begin: Time + + @required + end: Time +} + +@mixin +structure AuditMetadata { + @required + createdAt: Timestamp + + @required + updatedAt: Timestamp +} + +@mixin +resource AuditedResource { + properties: { + createdAt: Timestamp + updatedAt: Timestamp + } +} + +@mixin +@documentation("Parameters sent by the client to control pagination of the list results") +structure InputPagination { + @httpQuery("nextToken") + @documentation("An id used to retrieve the next page of results; leave empty for the first request") + nextToken: String + + @httpQuery("pageSize") + @default(100) + @documentation("The maximum number of items the client is requesting to be returned in this page") + pageSize: NaturalNumber +} + +@mixin +@documentation("Metadata returned to the client to assist in navigating paginated results") +structure OutputPagination { + @documentation("An id to be passed in the subsequent request to retrieve the next page; null if no more pages exist") + nextToken: String + + @required + @documentation("The actual number of items returned in the current response page.") + tokenCount: NaturalNumber +} diff --git a/smithy/model/main.smithy b/smithy/model/main.smithy index 4e67665..c5b925d 100644 --- a/smithy/model/main.smithy +++ b/smithy/model/main.smithy @@ -3,14 +3,25 @@ $version: "2" namespace com.shopping.inandout use aws.protocols#restJson1 -use com.shopping.inandout.brand#Brand -use com.shopping.inandout.tsp#FindTspSolution +use shopping.inandout.tsp#FindTspSolution +use shopping.inandout.article#Article +use shopping.inandout.brand#Brand +use shopping.inandout.offer#Offer +use shopping.inandout.product#Product +use shopping.inandout.stand#Stand +use shopping.inandout.store#Store +use shopping.inandout.tsp#FindTspSolution @restJson1 service InAndOut { version: "2026-04-01" resources: [ + Store Brand + Stand + Article + Product + Offer ] operations: [ FindTspSolution diff --git a/smithy/model/offer/offer-apis.smithy b/smithy/model/offer/offer-apis.smithy new file mode 100644 index 0000000..8b6d2e0 --- /dev/null +++ b/smithy/model/offer/offer-apis.smithy @@ -0,0 +1,95 @@ +$version: "2" + +namespace shopping.inandout.offer + +use shopping.inandout#InternalServerError +use shopping.inandout#InvalidInputError +use shopping.inandout#NaturalNumber +use shopping.inandout#Percentage +use shopping.inandout#ResourceAlreadyExistsError +use shopping.inandout#ResourceNotFoundError +use shopping.inandout#TimeRange +use shopping.inandout#UUID +use shopping.inandout#UUIDList + +// Does NOT contain the store data, only its id is necessary for logical grouping. +resource Offer { + identifiers: { + offerId: UUID + } + properties: { + storeId: UUID + percentage: Percentage + articleIdList: UUIDList + dependencyList: DependencyList + timeRange: TimeRange + lifetime: NaturalNumber + createdAt: Timestamp + updatedAt: Timestamp + } + create: CreateOffer + read: GetOffer + list: ListOffers + update: UpdateOffer + delete: DeleteOffer +} + +@http(method: "POST", uri: "/stores/{storeId}/offers") +operation CreateOffer { + input: CreateOfferInput + output: CreateOfferOutput + errors: [ + InvalidInputError + ResourceAlreadyExistsError + InternalServerError + ] +} + +@readonly +@http(method: "GET", uri: "/stores/{storeId}/offers/{offerId}") +operation GetOffer { + input: GetOfferInput + output: GetOfferOutput + errors: [ + InvalidInputError + ResourceNotFoundError + InternalServerError + ] +} + +@readonly +@paginated +@http(method: "GET", uri: "/stores/{storeId}/offers") +operation ListOffers { + input: ListOffersInput + output: ListOffersOutput + errors: [ + InvalidInputError + InternalServerError + ] +} + +@http(method: "PATCH", uri: "/stores/{storeId}/offers/{offerId}") +@documentation("Non-idempotent cascading operation, creates/deletes internal resources as needed") +operation UpdateOffer { + input: UpdateOfferInput + output: UpdateOfferOutput + errors: [ + InvalidInputError + ResourceNotFoundError + InternalServerError + ] +} + +@idempotent +@http(method: "DELETE", uri: "/stores/{storeId}/offers/{offerId}") +@documentation("Not restricted cascading operation, deletes discounts, dependencies, etc.") +operation DeleteOffer { + input: DeleteOfferInput + output: DeleteOfferOutput + errors: [ + InvalidInputError + ResourceNotFoundError + InternalServerError + ] +} diff --git a/smithy/model/offer/offer-io.smithy b/smithy/model/offer/offer-io.smithy new file mode 100644 index 0000000..4fb5407 --- /dev/null +++ b/smithy/model/offer/offer-io.smithy @@ -0,0 +1,52 @@ +$version: "2" + +namespace shopping.inandout.offer + +use shopping.inandout#InputPagination +use shopping.inandout#OutputPagination +use shopping.inandout#Percentage +use shopping.inandout#StoreIdMixin +use shopping.inandout#UUID + +structure CreateOfferInput with [StoreIdMixin, OfferInputMixin] { + @required + percentage: Percentage +} + +structure CreateOfferOutput { + @required + offerId: UUID +} + +structure GetOfferInput with [StoreIdMixin] { + @required + @httpLabel + offerId: UUID +} + +structure GetOfferOutput with [OfferOutputMixin] {} + +structure ListOffersInput with [StoreIdMixin, InputPagination] {} + +structure ListOffersOutput with [OutputPagination] { + @required + tokens: OfferSummaryList +} + +structure UpdateOfferInput with [StoreIdMixin, OfferInputMixin] { + @required + @httpLabel + offerId: UUID + + percentage: Percentage +} + +structure UpdateOfferOutput with [OfferOutputMixin] {} + +structure DeleteOfferInput with [StoreIdMixin] { + @required + @httpLabel + offerId: UUID +} + +structure DeleteOfferOutput {} diff --git a/smithy/model/offer/offer-types.smithy b/smithy/model/offer/offer-types.smithy new file mode 100644 index 0000000..41f78d7 --- /dev/null +++ b/smithy/model/offer/offer-types.smithy @@ -0,0 +1,61 @@ +$version: "2" + +namespace shopping.inandout.offer + +use shopping.inandout#AuditMetadata +use shopping.inandout#NaturalNumber +use shopping.inandout#Percentage +use shopping.inandout#TimeRange +use shopping.inandout#UUID +use shopping.inandout#UUIDList +use shopping.inandout.article#Article +use shopping.inandout.store#Store + +list DependencyList { + member: Dependency +} + +@references([ + { + resource: Article + } +]) +@documentation("Product dependency; it must be bought in order for the offer to activate") +structure Dependency { + articleId: UUID + quantity: NaturalNumber +} + +@mixin +structure OfferMixin { + articleIdList: UUIDList + dependencyList: DependencyList + timeRange: TimeRange + lifetime: NaturalNumber +} + +@mixin +structure OfferInputMixin with [OfferMixin] {} + +@mixin +@references([ + { + resource: Store + } +]) +structure OfferOutputMixin with [AuditMetadata, OfferMixin] { + @required + storeId: UUID + + @required + offerId: UUID + + @required + percentage: Percentage +} + +structure OfferSummary with [OfferOutputMixin] {} + +list OfferSummaryList { + member: OfferSummary +} diff --git a/smithy/model/product/product-apis.smithy b/smithy/model/product/product-apis.smithy new file mode 100644 index 0000000..a182bdc --- /dev/null +++ b/smithy/model/product/product-apis.smithy @@ -0,0 +1,81 @@ +$version: "2" + +namespace shopping.inandout.product + +use shopping.inandout#DeleteRestrictedError +use shopping.inandout#Description +use shopping.inandout#ImageUrl +use shopping.inandout#InternalServerError +use shopping.inandout#InvalidInputError +use shopping.inandout#ResourceAlreadyExistsError +use shopping.inandout#ResourceName +use shopping.inandout#ResourceNotFoundError +use shopping.inandout#UUID + +resource Product { + identifiers: { + productId: UUID + } + properties: { + name: ResourceName + subcategory: ResourceName + category: ResourceName + vendor: ResourceName + imageUrl: ImageUrl + description: Description + createdAt: Timestamp + updatedAt: Timestamp + } + create: CreateProduct + read: GetProduct + update: UpdateProduct + delete: DeleteProduct +} + +@http(method: "POST", uri: "/products") +operation CreateProduct { + input: CreateProductInput + output: CreateProductOutput + errors: [ + InvalidInputError + ResourceAlreadyExistsError + InternalServerError + ] +} + +@readonly +@http(method: "GET", uri: "/products/{productId}") +operation GetProduct { + input: GetProductInput + output: GetProductOutput + errors: [ + InvalidInputError + ResourceNotFoundError + InternalServerError + ] +} + +@http(method: "PATCH", uri: "/products/{productId}") +operation UpdateProduct { + input: UpdateProductInput + output: UpdateProductOutput + errors: [ + InvalidInputError + ResourceNotFoundError + InternalServerError + ] +} + +@idempotent +@http(method: "DELETE", uri: "/products/{productId}") +@documentation("Restricted cascading operation, references for articles should NOT exist") +operation DeleteProduct { + input: DeleteProductInput + output: DeleteProductOutput + errors: [ + InvalidInputError + ResourceNotFoundError + DeleteRestrictedError + InternalServerError + ] +} diff --git a/smithy/model/product/product-io.smithy b/smithy/model/product/product-io.smithy new file mode 100644 index 0000000..7ff40fa --- /dev/null +++ b/smithy/model/product/product-io.smithy @@ -0,0 +1,52 @@ +$version: "2" + +namespace shopping.inandout.product + +use shopping.inandout#ResourceName +use shopping.inandout#UUID + +structure CreateProductInput with [ProductInputMixin] { + @required + name: ResourceName + + @required + subcategory: ResourceName + + @required + category: ResourceName +} + +structure CreateProductOutput { + @required + productId: UUID +} + +structure GetProductInput { + @required + @httpLabel + productId: UUID +} + +structure GetProductOutput with [ProductOutputMixin] {} + +structure UpdateProductInput with [ProductInputMixin] { + @required + @httpLabel + productId: UUID + + name: ResourceName + + subcategory: ResourceName + + category: ResourceName +} + +structure UpdateProductOutput with [ProductOutputMixin] {} + +structure DeleteProductInput { + @required + @httpLabel + productId: UUID +} + +structure DeleteProductOutput {} diff --git a/smithy/model/product/product-types.smithy b/smithy/model/product/product-types.smithy new file mode 100644 index 0000000..28c3c9d --- /dev/null +++ b/smithy/model/product/product-types.smithy @@ -0,0 +1,41 @@ +$version: "2" + +namespace shopping.inandout.product + +use shopping.inandout#AuditMetadata +use shopping.inandout#Description +use shopping.inandout#ImageUrl +use shopping.inandout#ResourceName +use shopping.inandout#UUID + +@mixin +structure ProductMixin { + vendor: ResourceName + imageUrl: ImageUrl + description: Description +} + +@mixin +structure ProductInputMixin with [ProductMixin] {} + +@mixin +@references([ + { + resource: Product + } +]) +structure ProductOutputMixin with [AuditMetadata, ProductMixin] { + @required + productId: UUID + + @required + name: ResourceName + + @required + subcategory: ResourceName + + @required + category: ResourceName +} + +structure ProductSummary with [ProductOutputMixin] {} diff --git a/smithy/model/stand/stand-apis.smithy b/smithy/model/stand/stand-apis.smithy new file mode 100644 index 0000000..cf15348 --- /dev/null +++ b/smithy/model/stand/stand-apis.smithy @@ -0,0 +1,87 @@ +$version: "2" + +namespace shopping.inandout.stand + +use shopping.inandout#InternalServerError +use shopping.inandout#InvalidInputError +use shopping.inandout#PositiveDouble +use shopping.inandout#ResourceAlreadyExistsError +use shopping.inandout#ResourceNotFoundError +use shopping.inandout#UUID +use shopping.inandout.article#ArticleSummary + +resource Stand { + identifiers: { + standId: UUID + } + properties: { + edgeId: UUID + sourceNodeDistance: PositiveDouble + articleSummary: ArticleSummary + createdAt: Timestamp + updatedAt: Timestamp + } + create: CreateStand + read: GetStand + list: ListStands + update: UpdateStand + delete: DeleteStand +} + +@http(method: "POST", uri: "/stores/{storeId}/stands") +operation CreateStand { + input: CreateStandInput + output: CreateStandOutput + errors: [ + InvalidInputError + ResourceAlreadyExistsError + InternalServerError + ] +} + +@readonly +@http(method: "GET", uri: "/stores/{storeId}/stands/{standId}") +operation GetStand { + input: GetStandInput + output: GetStandOutput + errors: [ + InvalidInputError + ResourceNotFoundError + InternalServerError + ] +} + +@readonly +@paginated +@http(method: "GET", uri: "/stores/{storeId}/stands") +operation ListStands { + input: ListStandsInput + output: ListStandsOutput + errors: [ + InvalidInputError + InternalServerError + ] +} + +@http(method: "PATCH", uri: "/stores/{storeId}/stands/{standId}") +operation UpdateStand { + input: UpdateStandInput + output: UpdateStandOutput + errors: [ + InvalidInputError + ResourceNotFoundError + InternalServerError + ] +} + +@idempotent +@http(method: "DELETE", uri: "/stores/{storeId}/stands/{standId}") +operation DeleteStand { + input: DeleteStandInput + output: DeleteStandOutput + errors: [ + InvalidInputError + ResourceNotFoundError + InternalServerError + ] +} diff --git a/smithy/model/stand/stand-io.smithy b/smithy/model/stand/stand-io.smithy new file mode 100644 index 0000000..05447e3 --- /dev/null +++ b/smithy/model/stand/stand-io.smithy @@ -0,0 +1,85 @@ +$version: "2" + +namespace shopping.inandout.stand + +use shopping.inandout#InputPagination +use shopping.inandout#OutputPagination +use shopping.inandout#PositiveDouble +use shopping.inandout#StoreIdMixin +use shopping.inandout#UUID +use shopping.inandout.article#Article +use shopping.inandout.article#CreateArticleInput + +@references([ + { + resource: Article + } +]) +structure CreateStandInput with [StoreIdMixin] { + @required + edgeId: UUID + + @required + sourceNodeDistance: PositiveDouble + + // Clients must choose between providing an article id/details. + // Not none, not both, one field must be filled. + @notProperty + @documentation("Existing article referenced in a new stand") + articleId: UUID + + @notProperty + @documentation("Creates a new article") + createArticleInput: CreateArticleInput +} + +structure CreateStandOutput { + @required + standId: UUID +} + +structure GetStandInput with [StoreIdMixin] { + @required + @httpLabel + standId: UUID +} + +structure GetStandOutput with [StandOutputMixin] {} + +@references([ + { + resource: Article + } +]) +structure ListStandsInput with [StoreIdMixin, InputPagination] { + @httpQuery("edgeId") + edgeId: UUID + + @httpQuery("articleId") + articleId: UUID +} + +structure ListStandsOutput with [OutputPagination] { + @required + tokens: StandSummaryList +} + +structure UpdateStandInput with [StoreIdMixin] { + @required + @httpLabel + standId: UUID + + edgeId: UUID + + sourceNodeDistance: PositiveDouble +} + +structure UpdateStandOutput with [StandOutputMixin] {} + +structure DeleteStandInput with [StoreIdMixin] { + @required + @httpLabel + standId: UUID +} + +structure DeleteStandOutput {} diff --git a/smithy/model/stand/stand-types.smithy b/smithy/model/stand/stand-types.smithy new file mode 100644 index 0000000..bbc86b2 --- /dev/null +++ b/smithy/model/stand/stand-types.smithy @@ -0,0 +1,29 @@ +$version: "2" + +namespace shopping.inandout.stand + +use shopping.inandout#AuditMetadata +use shopping.inandout#PositiveDouble +use shopping.inandout#UUID +use shopping.inandout.article#ArticleSummary + +@mixin +structure StandOutputMixin with [AuditMetadata] { + @required + standId: UUID + + @required + edgeId: UUID + + @required + sourceNodeDistance: PositiveDouble + + @required + articleSummary: ArticleSummary +} + +structure StandSummary with [StandOutputMixin] {} + +list StandSummaryList { + member: StandSummary +} diff --git a/smithy/model/store/store-apis.smithy b/smithy/model/store/store-apis.smithy new file mode 100644 index 0000000..928268e --- /dev/null +++ b/smithy/model/store/store-apis.smithy @@ -0,0 +1,104 @@ +$version: "2" + +namespace shopping.inandout.store + +use shopping.inandout#Description +use shopping.inandout#ImageUrl +use shopping.inandout#InternalServerError +use shopping.inandout#InvalidInputError +use shopping.inandout#Latitude +use shopping.inandout#Longitude +use shopping.inandout#NaturalNumber +use shopping.inandout#ResourceAlreadyExistsError +use shopping.inandout#ResourceName +use shopping.inandout#ResourceNotFoundError +use shopping.inandout#UTCTimezone +use shopping.inandout#UUID +use shopping.inandout.brand#BrandSummary + +resource Store { + identifiers: { + storeId: UUID + } + properties: { + name: ResourceName + brandSummary: BrandSummary + description: Description + imageUrl: ImageUrl + timezone: UTCTimezone + operatingHoursMap: OperatingHoursMap + locationMapping: LocationMapping + mappingVersion: NaturalNumber + longitude: Longitude + latitude: Latitude + createdAt: Timestamp + updatedAt: Timestamp + } + create: CreateStore + read: GetStore + list: ListStores + update: UpdateStore + delete: DeleteStore +} + +@http(method: "POST", uri: "/stores") +operation CreateStore { + input: CreateStoreInput + output: CreateStoreOutput + errors: [ + InvalidInputError + ResourceAlreadyExistsError + InternalServerError + ] +} + +@readonly +@http(method: "GET", uri: "/stores/{storeId}") +@documentation("Returns additional brand details in order to avoid multiple network round-trips") +operation GetStore { + input: GetStoreInput + output: GetStoreOutput + errors: [ + InvalidInputError + ResourceNotFoundError + InternalServerError + ] +} + +@readonly +@paginated +@http(method: "GET", uri: "/stores") +@documentation("Returns additional brand details in order to avoid multiple network round-trips") +operation ListStores { + input: ListStoresInput + output: ListStoresOutput + errors: [ + InvalidInputError + InternalServerError + ] +} + +@http(method: "PATCH", uri: "/stores/{storeId}") +@documentation("Non-idempotent cascading operation, creates/deletes internal resources as needed") +operation UpdateStore { + input: UpdateStoreInput + output: UpdateStoreOutput + errors: [ + InvalidInputError + ResourceNotFoundError + InternalServerError + ] +} + +@idempotent +@http(method: "DELETE", uri: "/stores/{storeId}") +@documentation("Not restricted cascading operation, deletes floors, stands, etc.") +operation DeleteStore { + input: DeleteStoreInput + output: DeleteStoreOutput + errors: [ + InvalidInputError + ResourceNotFoundError + InternalServerError + ] +} diff --git a/smithy/model/store/store-io.smithy b/smithy/model/store/store-io.smithy new file mode 100644 index 0000000..1c62f76 --- /dev/null +++ b/smithy/model/store/store-io.smithy @@ -0,0 +1,79 @@ +$version: "2" + +namespace shopping.inandout.store + +use shopping.inandout#InputPagination +use shopping.inandout#Latitude +use shopping.inandout#Longitude +use shopping.inandout#NaturalNumber +use shopping.inandout#OutputPagination +use shopping.inandout#ResourceName +use shopping.inandout#UUID +use shopping.inandout.brand#Brand + +// The creation of the brand is independent of the creation of the store. +@references([ + { + resource: Brand + } +]) +structure CreateStoreInput with [StoreInputMixin] { + @required + @notProperty + brandId: UUID +} + +structure CreateStoreOutput { + @required + storeId: UUID +} + +structure GetStoreInput { + @required + @httpLabel + storeId: UUID +} + +structure GetStoreOutput with [StoreOutputMixin] {} + +@documentation("Retrieve a list of stores based on the provided queries") +structure ListStoresInput with [InputPagination] { + @httpQuery("name") + name: ResourceName + + @httpQuery("userLongitude") + userLongitude: Longitude + + @httpQuery("userLatitude") + userLatitude: Latitude + + // ! User location must be provided in order for the below queries to work. + @httpQuery("isOpen") + @documentation("Based on user location, his timezone is computed and then the list of open markets") + isOpen: Boolean + + @httpQuery("maxDistance") + @documentation("Distance measured in kilometers") + maxDistance: NaturalNumber +} + +structure ListStoresOutput with [OutputPagination] { + @required + tokens: StoreSummaryList +} + +structure UpdateStoreInput with [StoreInputMixin] { + @required + @httpLabel + storeId: UUID +} + +structure UpdateStoreOutput with [StoreOutputMixin] {} + +structure DeleteStoreInput { + @required + @httpLabel + storeId: UUID +} + +structure DeleteStoreOutput {} diff --git a/smithy/model/store/store-types.smithy b/smithy/model/store/store-types.smithy new file mode 100644 index 0000000..d236f30 --- /dev/null +++ b/smithy/model/store/store-types.smithy @@ -0,0 +1,107 @@ +$version: "2" + +namespace shopping.inandout.store + +use shopping.inandout#AuditMetadata +use shopping.inandout#DayType +use shopping.inandout#Description +use shopping.inandout#ImageUrl +use shopping.inandout#Latitude +use shopping.inandout#Longitude +use shopping.inandout#NaturalNumber +use shopping.inandout#ResourceName +use shopping.inandout#TimeRange +use shopping.inandout#UTCTimezone +use shopping.inandout#UUID +use shopping.inandout.brand#BrandSummary + +map OperatingHoursMap { + key: DayType + value: TimeRange +} + +structure LocationMapping { + floorList: FloorList +} + +list FloorList { + member: Floor +} + +structure Floor { + @required + floorId: UUID + + @required + level: Byte + + edgeList: EdgeList +} + +list EdgeList { + member: Edge +} + +structure Edge { + @required + sourceNode: Node + + @required + targetNode: Node + + name: ResourceName + + weight: Double +} + +structure Node { + @required + name: ResourceName + + type: NodeType +} + +enum NodeType { + NAVIGATION = "NAVIGATION" + ELEVATION = "ELEVATION" + DESCENT = "DESCENT" +} + +@mixin +structure StoreMixin { + description: Description + imageUrl: ImageUrl + timezone: UTCTimezone + operatingHoursMap: OperatingHoursMap + longitude: Longitude + latitude: Latitude +} + +@mixin +structure StoreInputMixin with [StoreMixin] { + name: ResourceName + locationMapping: LocationMapping +} + +@mixin +@documentation("Retrieves store and its associated brand details") +structure StoreOutputMixin with [AuditMetadata, StoreMixin] { + @required + storeId: UUID + + @required + name: ResourceName + + @required + brandSummary: BrandSummary + + @required + mappingVersion: NaturalNumber +} + +// Needed for the list method. +structure StoreSummary with [StoreOutputMixin] {} + +list StoreSummaryList { + member: StoreSummary +}