add manual openapi docs
This commit is contained in:
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"))
|
w.Write([]byte("1.0.0"))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
mux.HandleFunc("/openapi.json", ServeEmbeddedOpenAPI)
|
||||||
|
|
||||||
sigs := make(chan os.Signal, 1)
|
sigs := make(chan os.Signal, 1)
|
||||||
done := make(chan bool, 1)
|
done := make(chan bool, 1)
|
||||||
signal.Notify(sigs, syscall.SIGTERM)
|
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...)
|
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))
|
grain, err := s.Get(uint64(id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -42,7 +42,7 @@ func (s *PoolServer) HandleGet(w http.ResponseWriter, r *http.Request, id CartId
|
|||||||
return s.WriteResult(w, grain)
|
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")
|
sku := r.PathValue("sku")
|
||||||
msg, err := GetItemAddMessage(sku, 1, getCountryFromHost(r.Host), nil)
|
msg, err := GetItemAddMessage(sku, 1, getCountryFromHost(r.Host), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -72,7 +72,7 @@ func (s *PoolServer) WriteResult(w http.ResponseWriter, result *CartGrain) error
|
|||||||
return err
|
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")
|
itemIdString := r.PathValue("itemId")
|
||||||
itemId, err := strconv.Atoi(itemIdString)
|
itemId, err := strconv.Atoi(itemIdString)
|
||||||
@@ -92,7 +92,7 @@ type SetDeliveryRequest struct {
|
|||||||
PickupPoint *messages.PickupPoint `json:"pickupPoint,omitempty"`
|
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{}
|
delivery := SetDeliveryRequest{}
|
||||||
err := json.NewDecoder(r.Body).Decode(&delivery)
|
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)
|
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")
|
deliveryIdString := r.PathValue("deliveryId")
|
||||||
deliveryId, err := strconv.Atoi(deliveryIdString)
|
deliveryId, err := strconv.Atoi(deliveryIdString)
|
||||||
@@ -137,7 +137,7 @@ func (s *PoolServer) HandleSetPickupPoint(w http.ResponseWriter, r *http.Request
|
|||||||
return s.WriteResult(w, reply)
|
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")
|
deliveryIdString := r.PathValue("deliveryId")
|
||||||
deliveryId, err := strconv.Atoi(deliveryIdString)
|
deliveryId, err := strconv.Atoi(deliveryIdString)
|
||||||
@@ -151,7 +151,7 @@ func (s *PoolServer) HandleRemoveDelivery(w http.ResponseWriter, r *http.Request
|
|||||||
return s.WriteResult(w, reply)
|
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{}
|
changeQuantity := messages.ChangeQuantity{}
|
||||||
err := json.NewDecoder(r.Body).Decode(&changeQuantity)
|
err := json.NewDecoder(r.Body).Decode(&changeQuantity)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -196,7 +196,7 @@ func getMultipleAddMessages(items []Item, country string) []proto.Message {
|
|||||||
return msgs
|
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{}
|
setCartItems := SetCartItems{}
|
||||||
err := json.NewDecoder(r.Body).Decode(&setCartItems)
|
err := json.NewDecoder(r.Body).Decode(&setCartItems)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -214,7 +214,7 @@ func (s *PoolServer) HandleSetCartItems(w http.ResponseWriter, r *http.Request,
|
|||||||
return s.WriteResult(w, reply)
|
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{}
|
setCartItems := SetCartItems{}
|
||||||
err := json.NewDecoder(r.Body).Decode(&setCartItems)
|
err := json.NewDecoder(r.Body).Decode(&setCartItems)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -228,7 +228,7 @@ func (s *PoolServer) HandleAddMultiple(w http.ResponseWriter, r *http.Request, i
|
|||||||
return s.WriteResult(w, reply)
|
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{}
|
addRequest := messages.AddRequest{}
|
||||||
err := json.NewDecoder(r.Body).Decode(&addRequest)
|
err := json.NewDecoder(r.Body).Decode(&addRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -447,28 +447,28 @@ func (s *PoolServer) Serve() *http.ServeMux {
|
|||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
})
|
})
|
||||||
|
|
||||||
mux.HandleFunc("GET /", CookieCartIdHandler(s.ProxyHandler(s.HandleGet)))
|
mux.HandleFunc("GET /", CookieCartIdHandler(s.ProxyHandler(s.GetCartHandler)))
|
||||||
mux.HandleFunc("GET /add/{sku}", CookieCartIdHandler(s.ProxyHandler(s.HandleAddSku)))
|
mux.HandleFunc("GET /add/{sku}", CookieCartIdHandler(s.ProxyHandler(s.AddSkuToCartHandler)))
|
||||||
mux.HandleFunc("POST /add", CookieCartIdHandler(s.ProxyHandler(s.HandleAddMultiple)))
|
mux.HandleFunc("POST /add", CookieCartIdHandler(s.ProxyHandler(s.AddMultipleItemHandler)))
|
||||||
mux.HandleFunc("POST /", CookieCartIdHandler(s.ProxyHandler(s.HandleAddRequest)))
|
mux.HandleFunc("POST /", CookieCartIdHandler(s.ProxyHandler(s.AddSkuRequestHandler)))
|
||||||
mux.HandleFunc("POST /set", CookieCartIdHandler(s.ProxyHandler(s.HandleSetCartItems)))
|
mux.HandleFunc("POST /set", CookieCartIdHandler(s.ProxyHandler(s.SetCartItemsHandler)))
|
||||||
mux.HandleFunc("DELETE /{itemId}", CookieCartIdHandler(s.ProxyHandler(s.HandleDeleteItem)))
|
mux.HandleFunc("DELETE /{itemId}", CookieCartIdHandler(s.ProxyHandler(s.DeleteItemHandler)))
|
||||||
mux.HandleFunc("PUT /", CookieCartIdHandler(s.ProxyHandler(s.HandleQuantityChange)))
|
mux.HandleFunc("PUT /", CookieCartIdHandler(s.ProxyHandler(s.QuantityChangeHandler)))
|
||||||
mux.HandleFunc("DELETE /", CookieCartIdHandler(s.ProxyHandler(s.RemoveCartCookie)))
|
mux.HandleFunc("DELETE /", CookieCartIdHandler(s.ProxyHandler(s.RemoveCartCookie)))
|
||||||
mux.HandleFunc("POST /delivery", CookieCartIdHandler(s.ProxyHandler(s.HandleSetDelivery)))
|
mux.HandleFunc("POST /delivery", CookieCartIdHandler(s.ProxyHandler(s.SetDeliveryHandler)))
|
||||||
mux.HandleFunc("DELETE /delivery/{deliveryId}", CookieCartIdHandler(s.ProxyHandler(s.HandleRemoveDelivery)))
|
mux.HandleFunc("DELETE /delivery/{deliveryId}", CookieCartIdHandler(s.ProxyHandler(s.RemoveDeliveryHandler)))
|
||||||
mux.HandleFunc("PUT /delivery/{deliveryId}/pickupPoint", CookieCartIdHandler(s.ProxyHandler(s.HandleSetPickupPoint)))
|
mux.HandleFunc("PUT /delivery/{deliveryId}/pickupPoint", CookieCartIdHandler(s.ProxyHandler(s.SetPickupPointHandler)))
|
||||||
//mux.HandleFunc("GET /checkout", CookieCartIdHandler(s.ProxyHandler(s.HandleCheckout)))
|
//mux.HandleFunc("GET /checkout", CookieCartIdHandler(s.ProxyHandler(s.HandleCheckout)))
|
||||||
//mux.HandleFunc("GET /confirmation/{orderId}", CookieCartIdHandler(s.ProxyHandler(s.HandleConfirmation)))
|
//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}", CartIdHandler(s.ProxyHandler(s.GetCartHandler)))
|
||||||
mux.HandleFunc("GET /byid/{id}/add/{sku}", CartIdHandler(s.ProxyHandler(s.HandleAddSku)))
|
mux.HandleFunc("GET /byid/{id}/add/{sku}", CartIdHandler(s.ProxyHandler(s.AddSkuToCartHandler)))
|
||||||
mux.HandleFunc("POST /byid/{id}", CartIdHandler(s.ProxyHandler(s.HandleAddRequest)))
|
mux.HandleFunc("POST /byid/{id}", CartIdHandler(s.ProxyHandler(s.AddSkuRequestHandler)))
|
||||||
mux.HandleFunc("DELETE /byid/{id}/{itemId}", CartIdHandler(s.ProxyHandler(s.HandleDeleteItem)))
|
mux.HandleFunc("DELETE /byid/{id}/{itemId}", CartIdHandler(s.ProxyHandler(s.DeleteItemHandler)))
|
||||||
mux.HandleFunc("PUT /byid/{id}", CartIdHandler(s.ProxyHandler(s.HandleQuantityChange)))
|
mux.HandleFunc("PUT /byid/{id}", CartIdHandler(s.ProxyHandler(s.QuantityChangeHandler)))
|
||||||
mux.HandleFunc("POST /byid/{id}/delivery", CartIdHandler(s.ProxyHandler(s.HandleSetDelivery)))
|
mux.HandleFunc("POST /byid/{id}/delivery", CartIdHandler(s.ProxyHandler(s.SetDeliveryHandler)))
|
||||||
mux.HandleFunc("DELETE /byid/{id}/delivery/{deliveryId}", CartIdHandler(s.ProxyHandler(s.HandleRemoveDelivery)))
|
mux.HandleFunc("DELETE /byid/{id}/delivery/{deliveryId}", CartIdHandler(s.ProxyHandler(s.RemoveDeliveryHandler)))
|
||||||
mux.HandleFunc("PUT /byid/{id}/delivery/{deliveryId}/pickupPoint", CartIdHandler(s.ProxyHandler(s.HandleSetPickupPoint)))
|
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}/checkout", CartIdHandler(s.ProxyHandler(s.HandleCheckout)))
|
||||||
//mux.HandleFunc("GET /byid/{id}/confirmation", CartIdHandler(s.ProxyHandler(s.HandleConfirmation)))
|
//mux.HandleFunc("GET /byid/{id}/confirmation", CartIdHandler(s.ProxyHandler(s.HandleConfirmation)))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user