Complete refactor to new grpc control plane and only http proxy for carts #4
5
cmd/backoffice/main.go
Normal file
5
cmd/backoffice/main.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
// Your code here
|
||||
}
|
||||
@@ -396,6 +396,8 @@ func main() {
|
||||
w.Write([]byte("1.0.0"))
|
||||
})
|
||||
|
||||
mux.HandleFunc("/openapi.json", ServeEmbeddedOpenAPI)
|
||||
|
||||
sigs := make(chan os.Signal, 1)
|
||||
done := make(chan bool, 1)
|
||||
signal.Notify(sigs, syscall.SIGTERM)
|
||||
|
||||
677
cmd/cart/openapi.json
Normal file
677
cmd/cart/openapi.json
Normal file
@@ -0,0 +1,677 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Cart Service API",
|
||||
"description": "HTTP API for shopping cart operations (cookie-based or explicit id): retrieve cart, add/replace items, update quantity, manage deliveries.",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://cart.tornberg.me",
|
||||
"description": "Production server"
|
||||
},
|
||||
{
|
||||
"url": "http://localhost:8080",
|
||||
"description": "Local development (cart API mounted under /cart)"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/cart/": {
|
||||
"get": {
|
||||
"summary": "Get (or create) current cart (cookie based)",
|
||||
"description": "Returns the current cart. If no cartid cookie is present a new cart is created and Set-Cart-Id response header plus a Set-Cookie header are sent.",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Cart retrieved",
|
||||
"headers": {
|
||||
"Set-Cart-Id": {
|
||||
"description": "Returned when a new cart was created this request",
|
||||
"schema": { "type": "string" }
|
||||
},
|
||||
"X-Pod-Name": {
|
||||
"description": "Pod identifier serving the request",
|
||||
"schema": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/CartGrain" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": { "description": "Server error" }
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"summary": "Add single SKU (body)",
|
||||
"description": "Adds (or increases quantity of) a single SKU using request body.",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/AddRequest" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Item added",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/CartGrain" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": { "description": "Invalid request body" },
|
||||
"500": { "description": "Server error" }
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"summary": "Change quantity of an item",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/ChangeQuantity" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Quantity updated",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/CartGrain" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": { "description": "Invalid request body" },
|
||||
"500": { "description": "Server error" }
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"summary": "Clear cart cookie (logical cart reused only if referenced later)",
|
||||
"description": "Removes the cartid cookie by expiring it. Does not mutate server-side cart state.",
|
||||
"responses": {
|
||||
"200": { "description": "Cookie cleared (empty body)" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/cart/add/{sku}": {
|
||||
"get": {
|
||||
"summary": "Add a SKU (path)",
|
||||
"description": "Adds a single SKU with implicit quantity 1. Country inferred from Host header (-se / -no).",
|
||||
"parameters": [{ "$ref": "#/components/parameters/SkuParam" }],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Item added",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/CartGrain" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": { "description": "Server error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/cart/add": {
|
||||
"post": {
|
||||
"summary": "Add multiple items (append)",
|
||||
"description": "Adds multiple items to the cart without clearing existing contents.",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/SetCartItems" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Items added",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/CartGrain" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": { "description": "Invalid body" },
|
||||
"500": { "description": "Server error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/cart/set": {
|
||||
"post": {
|
||||
"summary": "Replace cart contents",
|
||||
"description": "Clears the cart first, then adds the provided items (idempotent with respect to target set).",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/SetCartItems" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Cart replaced",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/CartGrain" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": { "description": "Invalid body" },
|
||||
"500": { "description": "Server error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/cart/{itemId}": {
|
||||
"delete": {
|
||||
"summary": "Remove item by line id",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "itemId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": { "type": "integer", "format": "int64", "minimum": 0 },
|
||||
"description": "Internal cart line item identifier (not SKU)."
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Item removed",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/CartGrain" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": { "description": "Bad id" },
|
||||
"500": { "description": "Server error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/cart/delivery": {
|
||||
"post": {
|
||||
"summary": "Set (add) delivery",
|
||||
"description": "Adds a delivery option referencing one or more line item ids.",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/SetDeliveryRequest" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Delivery added/updated",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/CartGrain" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": { "description": "Invalid body" },
|
||||
"500": { "description": "Server error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/cart/delivery/{deliveryId}": {
|
||||
"delete": {
|
||||
"summary": "Remove delivery",
|
||||
"parameters": [{ "$ref": "#/components/parameters/DeliveryIdParam" }],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Delivery removed",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/CartGrain" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": { "description": "Bad id" },
|
||||
"500": { "description": "Server error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/cart/delivery/{deliveryId}/pickupPoint": {
|
||||
"put": {
|
||||
"summary": "Set pickup point for delivery",
|
||||
"parameters": [{ "$ref": "#/components/parameters/DeliveryIdParam" }],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/PickupPoint" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Pickup point set",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/CartGrain" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": { "description": "Invalid body" },
|
||||
"500": { "description": "Server error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/cart/byid/{id}": {
|
||||
"get": {
|
||||
"summary": "Get cart by explicit id",
|
||||
"parameters": [{ "$ref": "#/components/parameters/CartIdParam" }],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Cart retrieved",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/CartGrain" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": { "description": "Invalid id" },
|
||||
"500": { "description": "Server error" }
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"summary": "Add single SKU (body) by cart id",
|
||||
"parameters": [{ "$ref": "#/components/parameters/CartIdParam" }],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/AddRequest" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Item added",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/CartGrain" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": { "description": "Invalid body" },
|
||||
"500": { "description": "Server error" }
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"summary": "Change quantity (by id variant)",
|
||||
"parameters": [{ "$ref": "#/components/parameters/CartIdParam" }],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/ChangeQuantity" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Quantity updated",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/CartGrain" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": { "description": "Invalid body" },
|
||||
"500": { "description": "Server error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/cart/byid/{id}/add/{sku}": {
|
||||
"get": {
|
||||
"summary": "Add SKU (path) by explicit cart id",
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/CartIdParam" },
|
||||
{ "$ref": "#/components/parameters/SkuParam" }
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Item added",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/CartGrain" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": { "description": "Invalid id/sku" },
|
||||
"500": { "description": "Server error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/cart/byid/{id}/{itemId}": {
|
||||
"delete": {
|
||||
"summary": "Remove item (by id variant)",
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/CartIdParam" },
|
||||
{
|
||||
"name": "itemId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": { "type": "integer", "format": "int64", "minimum": 0 }
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Item removed",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/CartGrain" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": { "description": "Invalid id" },
|
||||
"500": { "description": "Server error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/cart/byid/{id}/delivery": {
|
||||
"post": {
|
||||
"summary": "Set delivery (by id variant)",
|
||||
"parameters": [{ "$ref": "#/components/parameters/CartIdParam" }],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/SetDeliveryRequest" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Delivery added/updated",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/CartGrain" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": { "description": "Invalid body" },
|
||||
"500": { "description": "Server error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/cart/byid/{id}/delivery/{deliveryId}": {
|
||||
"delete": {
|
||||
"summary": "Remove delivery (by id variant)",
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/CartIdParam" },
|
||||
{ "$ref": "#/components/parameters/DeliveryIdParam" }
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Delivery removed",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/CartGrain" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": { "description": "Invalid ids" },
|
||||
"500": { "description": "Server error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/cart/byid/{id}/delivery/{deliveryId}/pickupPoint": {
|
||||
"put": {
|
||||
"summary": "Set pickup point (by id variant)",
|
||||
"parameters": [
|
||||
{ "$ref": "#/components/parameters/CartIdParam" },
|
||||
{ "$ref": "#/components/parameters/DeliveryIdParam" }
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/PickupPoint" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Pickup point updated",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/CartGrain" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": { "description": "Invalid body" },
|
||||
"500": { "description": "Server error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/healthz": {
|
||||
"get": {
|
||||
"summary": "Liveness & capacity probe",
|
||||
"responses": {
|
||||
"200": { "description": "Healthy" },
|
||||
"500": { "description": "Unhealthy" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/readyz": {
|
||||
"get": {
|
||||
"summary": "Readiness probe",
|
||||
"responses": {
|
||||
"200": { "description": "Ready" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/livez": {
|
||||
"get": {
|
||||
"summary": "Liveness probe",
|
||||
"responses": {
|
||||
"200": { "description": "Alive" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/version": {
|
||||
"get": {
|
||||
"summary": "Service version",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Version string",
|
||||
"content": {
|
||||
"text/plain": {
|
||||
"schema": { "type": "string", "example": "1.0.0" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"parameters": {
|
||||
"SkuParam": {
|
||||
"name": "sku",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": { "type": "string" }
|
||||
},
|
||||
"CartIdParam": {
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "Base62 encoded cart id",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9A-Za-z]+$",
|
||||
"minLength": 1
|
||||
}
|
||||
},
|
||||
"DeliveryIdParam": {
|
||||
"name": "deliveryId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": { "type": "integer", "format": "int64", "minimum": 0 }
|
||||
}
|
||||
},
|
||||
"schemas": {
|
||||
"CartGrain": {
|
||||
"type": "object",
|
||||
"description": "Cart aggregate (actor state)",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Cart id (base62 encoded uint64)"
|
||||
},
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/components/schemas/CartItem" }
|
||||
},
|
||||
"totalPrice": { "type": "integer", "format": "int64" },
|
||||
"totalTax": { "type": "integer", "format": "int64" },
|
||||
"totalDiscount": { "type": "integer", "format": "int64" },
|
||||
"deliveries": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/components/schemas/CartDelivery" }
|
||||
},
|
||||
"processing": { "type": "boolean" },
|
||||
"paymentInProgress": { "type": "boolean" },
|
||||
"orderReference": { "type": "string" },
|
||||
"paymentStatus": { "type": "string" }
|
||||
},
|
||||
"required": ["id", "items", "totalPrice", "totalTax", "totalDiscount"]
|
||||
},
|
||||
"CartItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": { "type": "integer" },
|
||||
"itemId": { "type": "integer" },
|
||||
"parentId": { "type": "integer" },
|
||||
"sku": { "type": "string" },
|
||||
"name": { "type": "string" },
|
||||
"price": { "type": "integer", "format": "int64" },
|
||||
"totalPrice": { "type": "integer", "format": "int64" },
|
||||
"totalTax": { "type": "integer", "format": "int64" },
|
||||
"orgPrice": { "type": "integer", "format": "int64" },
|
||||
"stock": {
|
||||
"type": "integer",
|
||||
"description": "0=OutOfStock,1=LowStock,2=InStock"
|
||||
},
|
||||
"qty": { "type": "integer" },
|
||||
"tax": { "type": "integer" },
|
||||
"taxRate": { "type": "integer" },
|
||||
"brand": { "type": "string" },
|
||||
"category": { "type": "string" },
|
||||
"category2": { "type": "string" },
|
||||
"category3": { "type": "string" },
|
||||
"category4": { "type": "string" },
|
||||
"category5": { "type": "string" },
|
||||
"disclaimer": { "type": "string" },
|
||||
"sellerId": { "type": "string" },
|
||||
"sellerName": { "type": "string" },
|
||||
"type": { "type": "string", "description": "Article type" },
|
||||
"image": { "type": "string" },
|
||||
"outlet": { "type": "string", "nullable": true },
|
||||
"storeId": { "type": "string", "nullable": true }
|
||||
},
|
||||
"required": ["id", "sku", "name", "price", "qty", "tax"]
|
||||
},
|
||||
"CartDelivery": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": { "type": "integer" },
|
||||
"provider": { "type": "string" },
|
||||
"price": { "type": "integer", "format": "int64" },
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": { "type": "integer" }
|
||||
},
|
||||
"pickupPoint": { "$ref": "#/components/schemas/PickupPoint" }
|
||||
},
|
||||
"required": ["id", "provider", "price", "items"]
|
||||
},
|
||||
"PickupPoint": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": { "type": "string" },
|
||||
"name": { "type": "string", "nullable": true },
|
||||
"address": { "type": "string", "nullable": true },
|
||||
"city": { "type": "string", "nullable": true },
|
||||
"zip": { "type": "string", "nullable": true },
|
||||
"country": { "type": "string", "nullable": true }
|
||||
},
|
||||
"required": ["id"]
|
||||
},
|
||||
"AddRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"quantity": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"minimum": 1,
|
||||
"default": 1
|
||||
},
|
||||
"sku": { "type": "string" },
|
||||
"country": {
|
||||
"type": "string",
|
||||
"description": "Two-letter country code (inferred if omitted)"
|
||||
},
|
||||
"storeId": { "type": "string", "nullable": true }
|
||||
},
|
||||
"required": ["sku"]
|
||||
},
|
||||
"ChangeQuantity": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "Cart line item id"
|
||||
},
|
||||
"quantity": { "type": "integer", "format": "int32", "minimum": 0 }
|
||||
},
|
||||
"required": ["id", "quantity"]
|
||||
},
|
||||
"Item": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sku": { "type": "string" },
|
||||
"quantity": { "type": "integer", "minimum": 1 },
|
||||
"storeId": { "type": "string", "nullable": true }
|
||||
},
|
||||
"required": ["sku", "quantity"]
|
||||
},
|
||||
"SetCartItems": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"country": { "type": "string" },
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/components/schemas/Item" },
|
||||
"minItems": 0
|
||||
}
|
||||
},
|
||||
"required": ["items"]
|
||||
},
|
||||
"SetDeliveryRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"provider": { "type": "string" },
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": { "type": "integer", "format": "int64" },
|
||||
"description": "Line item ids served by this delivery"
|
||||
},
|
||||
"pickupPoint": { "$ref": "#/components/schemas/PickupPoint" }
|
||||
},
|
||||
"required": ["provider", "items"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [{ "name": "Cart" }, { "name": "Delivery" }, { "name": "System" }]
|
||||
}
|
||||
69
cmd/cart/openapi_embed.go
Normal file
69
cmd/cart/openapi_embed.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
_ "embed"
|
||||
"encoding/hex"
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// openapi_embed.go: Provides embedded OpenAPI spec and helper to mount handler.
|
||||
|
||||
//go:embed openapi.json
|
||||
var openapiJSON []byte
|
||||
|
||||
var (
|
||||
openapiOnce sync.Once
|
||||
openapiETag string
|
||||
)
|
||||
|
||||
// initOpenAPIMetadata computes immutable metadata for the embedded spec.
|
||||
func initOpenAPIMetadata() {
|
||||
sum := sha256.Sum256(openapiJSON)
|
||||
openapiETag = `W/"` + hex.EncodeToString(sum[:8]) + `"` // weak ETag with first 8 bytes
|
||||
}
|
||||
|
||||
// ServeEmbeddedOpenAPI serves the embedded OpenAPI JSON spec at /openapi.json.
|
||||
// It supports GET and HEAD and implements basic ETag caching.
|
||||
func ServeEmbeddedOpenAPI(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet && r.Method != http.MethodHead {
|
||||
w.Header().Set("Allow", "GET, HEAD")
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
openapiOnce.Do(initOpenAPIMetadata)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("ETag", openapiETag)
|
||||
|
||||
if match := r.Header.Get("If-None-Match"); match != "" {
|
||||
if bytes.Contains([]byte(match), []byte(openapiETag)) {
|
||||
w.WriteHeader(http.StatusNotModified)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if r.Method == http.MethodHead {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write(openapiJSON)
|
||||
}
|
||||
|
||||
// Optional: function to access raw spec bytes programmatically.
|
||||
func OpenAPISpecBytes() []byte {
|
||||
return openapiJSON
|
||||
}
|
||||
|
||||
// Optional: function to access current ETag.
|
||||
func OpenAPIETag() string {
|
||||
openapiOnce.Do(initOpenAPIMetadata)
|
||||
return openapiETag
|
||||
}
|
||||
@@ -33,7 +33,7 @@ func (s *PoolServer) ApplyLocal(id CartId, mutation ...proto.Message) (*CartGrai
|
||||
return s.Apply(uint64(id), mutation...)
|
||||
}
|
||||
|
||||
func (s *PoolServer) HandleGet(w http.ResponseWriter, r *http.Request, id CartId) error {
|
||||
func (s *PoolServer) GetCartHandler(w http.ResponseWriter, r *http.Request, id CartId) error {
|
||||
grain, err := s.Get(uint64(id))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -42,7 +42,7 @@ func (s *PoolServer) HandleGet(w http.ResponseWriter, r *http.Request, id CartId
|
||||
return s.WriteResult(w, grain)
|
||||
}
|
||||
|
||||
func (s *PoolServer) HandleAddSku(w http.ResponseWriter, r *http.Request, id CartId) error {
|
||||
func (s *PoolServer) AddSkuToCartHandler(w http.ResponseWriter, r *http.Request, id CartId) error {
|
||||
sku := r.PathValue("sku")
|
||||
msg, err := GetItemAddMessage(sku, 1, getCountryFromHost(r.Host), nil)
|
||||
if err != nil {
|
||||
@@ -72,7 +72,7 @@ func (s *PoolServer) WriteResult(w http.ResponseWriter, result *CartGrain) error
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *PoolServer) HandleDeleteItem(w http.ResponseWriter, r *http.Request, id CartId) error {
|
||||
func (s *PoolServer) DeleteItemHandler(w http.ResponseWriter, r *http.Request, id CartId) error {
|
||||
|
||||
itemIdString := r.PathValue("itemId")
|
||||
itemId, err := strconv.Atoi(itemIdString)
|
||||
@@ -92,7 +92,7 @@ type SetDeliveryRequest struct {
|
||||
PickupPoint *messages.PickupPoint `json:"pickupPoint,omitempty"`
|
||||
}
|
||||
|
||||
func (s *PoolServer) HandleSetDelivery(w http.ResponseWriter, r *http.Request, id CartId) error {
|
||||
func (s *PoolServer) SetDeliveryHandler(w http.ResponseWriter, r *http.Request, id CartId) error {
|
||||
|
||||
delivery := SetDeliveryRequest{}
|
||||
err := json.NewDecoder(r.Body).Decode(&delivery)
|
||||
@@ -110,7 +110,7 @@ func (s *PoolServer) HandleSetDelivery(w http.ResponseWriter, r *http.Request, i
|
||||
return s.WriteResult(w, data)
|
||||
}
|
||||
|
||||
func (s *PoolServer) HandleSetPickupPoint(w http.ResponseWriter, r *http.Request, id CartId) error {
|
||||
func (s *PoolServer) SetPickupPointHandler(w http.ResponseWriter, r *http.Request, id CartId) error {
|
||||
|
||||
deliveryIdString := r.PathValue("deliveryId")
|
||||
deliveryId, err := strconv.Atoi(deliveryIdString)
|
||||
@@ -137,7 +137,7 @@ func (s *PoolServer) HandleSetPickupPoint(w http.ResponseWriter, r *http.Request
|
||||
return s.WriteResult(w, reply)
|
||||
}
|
||||
|
||||
func (s *PoolServer) HandleRemoveDelivery(w http.ResponseWriter, r *http.Request, id CartId) error {
|
||||
func (s *PoolServer) RemoveDeliveryHandler(w http.ResponseWriter, r *http.Request, id CartId) error {
|
||||
|
||||
deliveryIdString := r.PathValue("deliveryId")
|
||||
deliveryId, err := strconv.Atoi(deliveryIdString)
|
||||
@@ -151,7 +151,7 @@ func (s *PoolServer) HandleRemoveDelivery(w http.ResponseWriter, r *http.Request
|
||||
return s.WriteResult(w, reply)
|
||||
}
|
||||
|
||||
func (s *PoolServer) HandleQuantityChange(w http.ResponseWriter, r *http.Request, id CartId) error {
|
||||
func (s *PoolServer) QuantityChangeHandler(w http.ResponseWriter, r *http.Request, id CartId) error {
|
||||
changeQuantity := messages.ChangeQuantity{}
|
||||
err := json.NewDecoder(r.Body).Decode(&changeQuantity)
|
||||
if err != nil {
|
||||
@@ -196,7 +196,7 @@ func getMultipleAddMessages(items []Item, country string) []proto.Message {
|
||||
return msgs
|
||||
}
|
||||
|
||||
func (s *PoolServer) HandleSetCartItems(w http.ResponseWriter, r *http.Request, id CartId) error {
|
||||
func (s *PoolServer) SetCartItemsHandler(w http.ResponseWriter, r *http.Request, id CartId) error {
|
||||
setCartItems := SetCartItems{}
|
||||
err := json.NewDecoder(r.Body).Decode(&setCartItems)
|
||||
if err != nil {
|
||||
@@ -214,7 +214,7 @@ func (s *PoolServer) HandleSetCartItems(w http.ResponseWriter, r *http.Request,
|
||||
return s.WriteResult(w, reply)
|
||||
}
|
||||
|
||||
func (s *PoolServer) HandleAddMultiple(w http.ResponseWriter, r *http.Request, id CartId) error {
|
||||
func (s *PoolServer) AddMultipleItemHandler(w http.ResponseWriter, r *http.Request, id CartId) error {
|
||||
setCartItems := SetCartItems{}
|
||||
err := json.NewDecoder(r.Body).Decode(&setCartItems)
|
||||
if err != nil {
|
||||
@@ -228,7 +228,7 @@ func (s *PoolServer) HandleAddMultiple(w http.ResponseWriter, r *http.Request, i
|
||||
return s.WriteResult(w, reply)
|
||||
}
|
||||
|
||||
func (s *PoolServer) HandleAddRequest(w http.ResponseWriter, r *http.Request, id CartId) error {
|
||||
func (s *PoolServer) AddSkuRequestHandler(w http.ResponseWriter, r *http.Request, id CartId) error {
|
||||
addRequest := messages.AddRequest{}
|
||||
err := json.NewDecoder(r.Body).Decode(&addRequest)
|
||||
if err != nil {
|
||||
@@ -447,28 +447,28 @@ func (s *PoolServer) Serve() *http.ServeMux {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
|
||||
mux.HandleFunc("GET /", CookieCartIdHandler(s.ProxyHandler(s.HandleGet)))
|
||||
mux.HandleFunc("GET /add/{sku}", CookieCartIdHandler(s.ProxyHandler(s.HandleAddSku)))
|
||||
mux.HandleFunc("POST /add", CookieCartIdHandler(s.ProxyHandler(s.HandleAddMultiple)))
|
||||
mux.HandleFunc("POST /", CookieCartIdHandler(s.ProxyHandler(s.HandleAddRequest)))
|
||||
mux.HandleFunc("POST /set", CookieCartIdHandler(s.ProxyHandler(s.HandleSetCartItems)))
|
||||
mux.HandleFunc("DELETE /{itemId}", CookieCartIdHandler(s.ProxyHandler(s.HandleDeleteItem)))
|
||||
mux.HandleFunc("PUT /", CookieCartIdHandler(s.ProxyHandler(s.HandleQuantityChange)))
|
||||
mux.HandleFunc("GET /", CookieCartIdHandler(s.ProxyHandler(s.GetCartHandler)))
|
||||
mux.HandleFunc("GET /add/{sku}", CookieCartIdHandler(s.ProxyHandler(s.AddSkuToCartHandler)))
|
||||
mux.HandleFunc("POST /add", CookieCartIdHandler(s.ProxyHandler(s.AddMultipleItemHandler)))
|
||||
mux.HandleFunc("POST /", CookieCartIdHandler(s.ProxyHandler(s.AddSkuRequestHandler)))
|
||||
mux.HandleFunc("POST /set", CookieCartIdHandler(s.ProxyHandler(s.SetCartItemsHandler)))
|
||||
mux.HandleFunc("DELETE /{itemId}", CookieCartIdHandler(s.ProxyHandler(s.DeleteItemHandler)))
|
||||
mux.HandleFunc("PUT /", CookieCartIdHandler(s.ProxyHandler(s.QuantityChangeHandler)))
|
||||
mux.HandleFunc("DELETE /", CookieCartIdHandler(s.ProxyHandler(s.RemoveCartCookie)))
|
||||
mux.HandleFunc("POST /delivery", CookieCartIdHandler(s.ProxyHandler(s.HandleSetDelivery)))
|
||||
mux.HandleFunc("DELETE /delivery/{deliveryId}", CookieCartIdHandler(s.ProxyHandler(s.HandleRemoveDelivery)))
|
||||
mux.HandleFunc("PUT /delivery/{deliveryId}/pickupPoint", CookieCartIdHandler(s.ProxyHandler(s.HandleSetPickupPoint)))
|
||||
mux.HandleFunc("POST /delivery", CookieCartIdHandler(s.ProxyHandler(s.SetDeliveryHandler)))
|
||||
mux.HandleFunc("DELETE /delivery/{deliveryId}", CookieCartIdHandler(s.ProxyHandler(s.RemoveDeliveryHandler)))
|
||||
mux.HandleFunc("PUT /delivery/{deliveryId}/pickupPoint", CookieCartIdHandler(s.ProxyHandler(s.SetPickupPointHandler)))
|
||||
//mux.HandleFunc("GET /checkout", CookieCartIdHandler(s.ProxyHandler(s.HandleCheckout)))
|
||||
//mux.HandleFunc("GET /confirmation/{orderId}", CookieCartIdHandler(s.ProxyHandler(s.HandleConfirmation)))
|
||||
|
||||
mux.HandleFunc("GET /byid/{id}", CartIdHandler(s.ProxyHandler(s.HandleGet)))
|
||||
mux.HandleFunc("GET /byid/{id}/add/{sku}", CartIdHandler(s.ProxyHandler(s.HandleAddSku)))
|
||||
mux.HandleFunc("POST /byid/{id}", CartIdHandler(s.ProxyHandler(s.HandleAddRequest)))
|
||||
mux.HandleFunc("DELETE /byid/{id}/{itemId}", CartIdHandler(s.ProxyHandler(s.HandleDeleteItem)))
|
||||
mux.HandleFunc("PUT /byid/{id}", CartIdHandler(s.ProxyHandler(s.HandleQuantityChange)))
|
||||
mux.HandleFunc("POST /byid/{id}/delivery", CartIdHandler(s.ProxyHandler(s.HandleSetDelivery)))
|
||||
mux.HandleFunc("DELETE /byid/{id}/delivery/{deliveryId}", CartIdHandler(s.ProxyHandler(s.HandleRemoveDelivery)))
|
||||
mux.HandleFunc("PUT /byid/{id}/delivery/{deliveryId}/pickupPoint", CartIdHandler(s.ProxyHandler(s.HandleSetPickupPoint)))
|
||||
mux.HandleFunc("GET /byid/{id}", CartIdHandler(s.ProxyHandler(s.GetCartHandler)))
|
||||
mux.HandleFunc("GET /byid/{id}/add/{sku}", CartIdHandler(s.ProxyHandler(s.AddSkuToCartHandler)))
|
||||
mux.HandleFunc("POST /byid/{id}", CartIdHandler(s.ProxyHandler(s.AddSkuRequestHandler)))
|
||||
mux.HandleFunc("DELETE /byid/{id}/{itemId}", CartIdHandler(s.ProxyHandler(s.DeleteItemHandler)))
|
||||
mux.HandleFunc("PUT /byid/{id}", CartIdHandler(s.ProxyHandler(s.QuantityChangeHandler)))
|
||||
mux.HandleFunc("POST /byid/{id}/delivery", CartIdHandler(s.ProxyHandler(s.SetDeliveryHandler)))
|
||||
mux.HandleFunc("DELETE /byid/{id}/delivery/{deliveryId}", CartIdHandler(s.ProxyHandler(s.RemoveDeliveryHandler)))
|
||||
mux.HandleFunc("PUT /byid/{id}/delivery/{deliveryId}/pickupPoint", CartIdHandler(s.ProxyHandler(s.SetPickupPointHandler)))
|
||||
//mux.HandleFunc("GET /byid/{id}/checkout", CartIdHandler(s.ProxyHandler(s.HandleCheckout)))
|
||||
//mux.HandleFunc("GET /byid/{id}/confirmation", CartIdHandler(s.ProxyHandler(s.HandleConfirmation)))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user