package api import "git.threesix.ai/jordan/sp3-verify-1770325830/pkg/openapi" // NewServiceSpec builds the OpenAPI specification for the chat-api service. func NewServiceSpec() *openapi.OpenAPISpec { spec := openapi.NewOpenAPISpec("chat-api API", "1.0.0"). WithDescription("REST API for the chat-api service"). WithBearerSecurity("bearer", "JWT authentication token"). WithTag("Health", "Service health endpoints"). WithTag("Examples", "Example CRUD endpoints"). WithTag("WebSocket", "Real-time WebSocket endpoints") // Define reusable schemas spec.WithSchema("Example", openapi.Object(map[string]openapi.Schema{ "id": openapi.UUID().WithDescription("Unique identifier"), "name": openapi.String().WithDescription("Name of the example").WithExample("My Example"), "description": openapi.String().WithDescription("Optional description").WithExample("A description"), "created_at": openapi.DateTime().WithDescription("Creation timestamp"), "updated_at": openapi.DateTime().WithDescription("Last update timestamp"), }, "id", "name")) spec.WithSchema("CreateExampleRequest", openapi.Object(map[string]openapi.Schema{ "name": openapi.StringWithMinMax(1, 100).WithDescription("Name of the example"), "description": openapi.StringWithMinMax(0, 500).WithDescription("Optional description"), }, "name")) spec.WithSchema("UpdateExampleRequest", openapi.Object(map[string]openapi.Schema{ "name": openapi.StringWithMinMax(1, 100).WithDescription("Updated name"), "description": openapi.StringWithMinMax(0, 500).WithDescription("Updated description"), })) // Health spec.AddPath("/api/chat-api/health", "get", map[string]any{ "summary": "Health check", "tags": []string{"Health"}, "responses": map[string]any{ "200": openapi.OpResponse("Service is healthy", openapi.Object(map[string]openapi.Schema{ "service": openapi.String(), "status": openapi.String(), })), }, }) // List examples spec.AddPath("/api/chat-api/examples", "get", map[string]any{ "summary": "List examples", "description": "Returns a paginated list of examples.", "tags": []string{"Examples"}, "parameters": []any{openapi.PageParam(), openapi.PerPageParam()}, "responses": map[string]any{ "200": openapi.OpResponse("Success", openapi.ResponseSchema(openapi.RefArray("Example"))), }, }) // Get example spec.AddPath("/api/chat-api/examples/{id}", "get", map[string]any{ "summary": "Get example by ID", "tags": []string{"Examples"}, "parameters": []any{openapi.IDParam()}, "responses": map[string]any{ "200": openapi.OpResponse("Success", openapi.ResponseSchema(openapi.Ref("Example"))), "404": openapi.OpResponse("Not found", openapi.ErrorResponseSchema()), }, }) // Create example spec.AddPath("/api/chat-api/examples", "post", map[string]any{ "summary": "Create example", "description": "Creates a new example. Requires authentication.", "tags": []string{"Examples"}, "security": []map[string][]string{{"bearer": {}}}, "requestBody": openapi.RequestBody(openapi.Ref("CreateExampleRequest"), true), "responses": map[string]any{ "201": openapi.OpResponse("Created", openapi.ResponseSchema(openapi.Ref("Example"))), "400": openapi.OpResponse("Bad request", openapi.ErrorResponseSchema()), "401": openapi.OpResponse("Unauthorized", openapi.ErrorResponseSchema()), "422": openapi.OpResponse("Validation error", openapi.ErrorResponseSchema()), }, }) // Update example spec.AddPath("/api/chat-api/examples/{id}", "put", map[string]any{ "summary": "Update example", "description": "Updates an existing example. Requires authentication.", "tags": []string{"Examples"}, "security": []map[string][]string{{"bearer": {}}}, "parameters": []any{openapi.IDParam()}, "requestBody": openapi.RequestBody(openapi.Ref("UpdateExampleRequest"), true), "responses": map[string]any{ "200": openapi.OpResponse("Updated", openapi.ResponseSchema(openapi.Ref("Example"))), "400": openapi.OpResponse("Bad request", openapi.ErrorResponseSchema()), "401": openapi.OpResponse("Unauthorized", openapi.ErrorResponseSchema()), "404": openapi.OpResponse("Not found", openapi.ErrorResponseSchema()), }, }) // Delete example spec.AddPath("/api/chat-api/examples/{id}", "delete", map[string]any{ "summary": "Delete example", "description": "Deletes an example by ID. Requires authentication.", "tags": []string{"Examples"}, "security": []map[string][]string{{"bearer": {}}}, "parameters": []any{openapi.IDParam()}, "responses": map[string]any{ "204": openapi.OpResponseNoContent(), "401": openapi.OpResponse("Unauthorized", openapi.ErrorResponseSchema()), "404": openapi.OpResponse("Not found", openapi.ErrorResponseSchema()), }, }) // WebSocket message schema spec.WithSchema("WebSocketMessage", openapi.Object(map[string]openapi.Schema{ "id": openapi.String().WithDescription("Message ID (set by server)"), "type": openapi.String().WithDescription("Message type: chat, presence, notification, system, error, ping, pong"), "room": openapi.String().WithDescription("Target room (optional)"), "from": openapi.String().WithDescription("Sender client ID (set by server)"), "data": openapi.Object(nil).WithDescription("Message payload"), "timestamp": openapi.DateTime().WithDescription("Message timestamp"), }, "type")) // WebSocket connection spec.AddPath("/api/chat-api/ws", "get", map[string]any{ "summary": "WebSocket connection", "description": "Upgrades to WebSocket for real-time chat. Messages are broadcast to all connected clients via Redis pub/sub. Send JSON messages matching the WebSocketMessage schema.", "tags": []string{"WebSocket"}, "responses": map[string]any{ "101": map[string]any{ "description": "Switching Protocols - WebSocket upgrade successful", }, "401": openapi.OpResponse("Unauthorized - authentication required (when AUTH_ENABLED=true)", openapi.ErrorResponseSchema()), }, }) // WebSocket connection to room spec.AddPath("/api/chat-api/ws/{room}", "get", map[string]any{ "summary": "WebSocket connection to room", "description": "Upgrades to WebSocket and joins the specified room. Messages are broadcast only to clients in the same room.", "tags": []string{"WebSocket"}, "parameters": []any{ map[string]any{ "name": "room", "in": "path", "required": true, "description": "Room identifier to join", "schema": map[string]any{"type": "string"}, }, }, "responses": map[string]any{ "101": map[string]any{ "description": "Switching Protocols - WebSocket upgrade successful", }, "401": openapi.OpResponse("Unauthorized - authentication required (when AUTH_ENABLED=true)", openapi.ErrorResponseSchema()), }, }) return spec }