Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 216 additions & 0 deletions server/shop/catalog/v1/service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ service CatalogService {
]
};
}
rpc GetOfferDetails(GetOfferDetailsRequest) returns (GetOfferDetailsResponse) {
option (google.api.http) = {get: "/api/v1/catalog/offer"};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
summary: "Get offer details"
description: "Retrieves detailed information about a specific offer, including metadata and tabbed content with structured data tables."
security: [
{
security_requirement: {key: "apiKey"}
}
]
};
}
}

message GetCatalogRequest {
Expand Down Expand Up @@ -627,3 +639,207 @@ message GetCatalogResponse {
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "The product catalog organized in sections"}
];
}

message GetOfferDetailsRequest {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
json_schema: {
title: "Get Offer Details Request"
description: "Request to retrieve detailed information about a specific offer"
required: [
"guid",
"product_id"
]
}
};
string guid = 1 [
(google.api.field_behavior) = REQUIRED,
(validate.rules).string.uuid = true,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "The offer/product GUID (can be auto-generated UUID v5 if not provided in catalog response)"}
];
string product_id = 2 [
(google.api.field_behavior) = REQUIRED,
(validate.rules).string.min_len = 1,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "The offer/product SKU"}
];
optional string region = 3 [
(validate.rules).string = {
min_len: 2
max_len: 3
ignore_empty: true
},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "Optional region filter using ISO-3166-1 alpha-2 country code (e.g., 'US', 'EU', 'JP')"}
];
optional string language = 4 [

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we may need to use the 4 letter code here i think to differentiate between regional languages like es-ES vs es-MX

(validate.rules).string = {
min_len: 2
max_len: 3
ignore_empty: true
},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "Optional language code filter using ISO 639-1 code (e.g., 'en', 'es', 'fr', 'de')"}
];
}

message GetOfferDetailsResponse {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
json_schema: {
title: "Get Offer Details Response"
description: "Contains detailed information about a specific offer"
required: ["metadata"]
}
};
oneof offer_identifier {
string guid = 1 [
(validate.rules).string = {
uuid: true
ignore_empty: true
},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "The offer/product GUID (providing this OR product_id is required)"}
];
string product_id = 2 [
(validate.rules).string = {
min_len: 1
ignore_empty: true
},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "The offer/product SKU (providing this OR guid is required)"}
];
}

message LocalizableText {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
json_schema: {
title: "Localizable Text"
description: "Text content that can be localized for a specific language"
}
};
string default_text = 1 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "Default text content, usually in English (fallback if no localized text is provided or if region/language is not supported or cannot be determined)"}];
oneof localization {
string localization_key = 2 [
(validate.rules).string = {
min_len: 1
ignore_empty: true
},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "The unique key used to identify the localized text content for a specific language"}
];
string localized_text = 3 [
(validate.rules).string = {
min_len: 1
ignore_empty: true
},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "Localized text content for the language specified by the code sent in the request filters"}
];
}
}

message OfferMetadata {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
json_schema: {
title: "Offer Metadata"
description: "Top section metadata for the offer details view"
}
};
LocalizableText title = 1 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "Offer title"}];
LocalizableText description = 2 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "Optional offer description"}];
optional string image_url = 3 [
(validate.rules).string = {
uri: true
ignore_empty: true
},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "Optional URL to offer image"}
];
optional string background_style = 4 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "Optional background styling for metadata section"}];
}
OfferMetadata metadata = 3 [

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as we discussed in our last 1:1 it looks like we also need to add in an array of some sort to represent the list of sections in the metadata and their contents. Note that in some cases text is uppercased vs not so we need a way to configure that as well

Screenshot 2025-11-20 at 12 00 10 PM

(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "Metadata displayed in the top section of the offer details view"}
];

message Table {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
json_schema: {
title: "Table"
description: "Represents a table with headers and rows"
required: [
"headers",
"rows"
]
}
};
message HeaderCell {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
json_schema: {
title: "Header Cell"
description: "Represents a header cell in a table"
}
};
LocalizableText text = 1 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "Header text content"}];
optional uint32 relative_width = 2 [

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we going to validate that the widths of all the headers add to 100? if not how do you envision we handle that?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also if not provided, should we assume the space is evenly distributed then?

(validate.rules).uint32 = {
gte: 1
lte: 100
},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "Optional relative width as percentage (1-100), used to distribute column widths"}
];
optional string background_style = 3 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "Optional CSS-style background styling for the header cell"}];
optional string text_style = 4 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "Optional CSS-style text styling for the header cell"}];
}
repeated HeaderCell headers = 1 [
(google.api.field_behavior) = REQUIRED,
(validate.rules).repeated.min_items = 1,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "Header cells for the table columns"}
];

message TableRow {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
json_schema: {
title: "TableRow"
description: "Represents a single row in a table"
required: ["cells"]
}
};
message TableCell {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
json_schema: {
title: "TableCell"
description: "Represents a single cell in a table row with content and styling"
}
};
optional LocalizableText primary_text = 1 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "Primary text content for the cell"}];
optional LocalizableText sub_text = 2 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "Optional secondary text content for the cell"}];
optional string icon_image_url = 3 [
(validate.rules).string = {
uri: true
ignore_empty: true
},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "Optional URL to icon image for the cell"}
];
optional string background_style = 4 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "Optional CSS-style background styling for the cell"}];
optional string text_style = 5 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "Optional CSS-style text styling for the cell"}];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is an example format of how you see this being used? there are different text properties that could be configured like text-transform​, color​, etc. would those be combined into one string here or should this be for a more specific text styling?

}
repeated TableCell cells = 1 [
(google.api.field_behavior) = REQUIRED,
(validate.rules).repeated.min_items = 1,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "Cells in this row, displayed in the order provided"}
];
}
repeated TableRow rows = 2 [
(google.api.field_behavior) = REQUIRED,
(validate.rules).repeated.min_items = 1,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "Rows in the table, displayed in the order provided"}
];
}

message Tab {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this needs to also have a tab title right? (LocalizableText)

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
json_schema: {
title: "Tab"
description: "Represents a tab containing multiple tables"
required: ["tables"]
}
};
map<string, Table> tables = 1 [
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "Map of table identifiers to Table objects. Keys can be used for ordering or identification purposes."}
];
}
map<string, Tab> tabs = 4 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "Map of tab identifiers to Tab objects containing tabbed content. Keys can be used for tab ordering or identification purposes."}];
}