openapi: 3.0.3 info: title: "Distributed Arcade" version: 1.0.0 description: |- A super-fast high score gatherer using Rust and Redis. It allows the creation of _boards_, [sorted sets](https://redis.io/docs/data-types/sorted-sets/) that can be used to track the scores of players in videogames. It is written to be extremely fast and scalable: it should be able to receive bursts of many requests. Errors can be distinguished from successful requests via HTTP status codes >=400. contact: name: "Stefano Pigozzi" url: "https://www.steffo.eu" email: "me@steffo.eu" license: name: "GNU Affero General Public License v3.0 or later" url: "https://github.com/Steffo99/distributed-arcade/blob/main/LICENSE.txt" servers: - url: "https://arcade.steffo.eu" description: "Experimental production server" - url: "http://127.0.0.1:30000" description: "Local development server" tags: - name: "Home" description: "Launch checklist" - name: "Board" description: "About boards" - name: "Score" description: "Submit scores" paths: /: get: operationId: "getHome" summary: "Verify that the web server is working as expected" description: |- This method simply echoes back a response, and can be used to verify that the API web server is working. tags: ["Home"] responses: 204: description: "Working as expected" post: operationId: "postHome" summary: "Verify that everything is working as expected" description: |- This method is like `GET /`, but it also `PING`s the Redis server to ensure it is working and configured properly. tags: ["Home"] responses: 204: description: "Working as expected" 500: description: "Did not receive `PONG` from redis" content: application/json: schema: type: string example: "Redis gave an unexpected response" 502: $ref: "#/components/responses/RedisCmdFailed" 504: $ref: "#/components/responses/RedisConnFailed" /board/: get: operationId: "getBoard" summary: "Get the scores of a board" description: |- This method requests a page of scores from a board using the [`ZSCAN`](https://redis.io/commands/zscan/) Redis command. An offset must be specified to start returning scores from a certain index. The number of responses to return must be specified as well. tags: ["Board"] parameters: - $ref: "#/components/parameters/board" - $ref: "#/components/parameters/offset" - $ref: "#/components/parameters/size" responses: 200: description: "Scores retrieved successfully" content: application/json: schema: type: array items: type: object description: "A score submitted by an user." properties: name: type: string description: "The name of the user who submitted the score." example: "Steffo" score: type: number description: "The submitted score." example: 1234.56 502: $ref: "#/components/responses/RedisCmdFailed" 504: $ref: "#/components/responses/RedisConnFailed" post: operationId: "postBoard" summary: "Create a new board" description: |- This method creates a new board. It returns its _score submission token_ (`XBoardToken` in this spec), which is required to submit new scores to the board. Boards can use two different orders: - using the `Ascending` order, lower scores are better ranked than higher scores, like in racing games or golf; - using the `Descending` order, higher scores are better ranked than lower scores, like in arcade games or athletics. **WARNING: Once created, a board cannot be edited or deleted, and its token will not be accessible any longer!** Requires an authorization key, set as the `CREATE_TOKEN` environment variable of the server. tags: ["Board"] requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: "The name of the board to create. It will be converted to kebab-case." example: "gravityfusion" order: type: string example: "Descending" description: "The ordering of the board, either ascending or descending." enum: - "Ascending" - "Descending" security: - XCreateToken: [] responses: 201: description: "Board created successfully" content: application/json: schema: type: string example: "W4SbhbJ3tnGaIM1S" links: getScore: operationId: "getScore" parameters: board: "$response.body/name" putScore: operationId: "putScore" parameters: board: "$response.body/name" 401: description: "Missing, invalid or malformed Authorization header" content: application/json: schema: type: string example: "Missing Authorization header" 403: description: "Invalid create token" content: application/json: schema: type: string example: "Invalid create token" 409: description: "Board already exists" content: application/json: schema: type: string example: "Board already exists" 500: description: "Could not generate secure board token" content: application/json: schema: type: string example: "Could not generate secure board token" 502: $ref: "#/components/responses/RedisCmdFailed" 504: $ref: "#/components/responses/RedisConnFailed" /score/: get: operationId: "getScore" summary: "Get a score from a board" description: |- Retrieve the score and the position that the given user has on the leaderboard. tags: ["Score"] parameters: - $ref: "#/components/parameters/board" - $ref: "#/components/parameters/player" responses: 200: description: "Score retrieved successfully" content: application/json: schema: type: object properties: score: type: number description: "The score of the specified player." example: 1234.56 rank: type: integer description: "The zero-indexed rank of the specified player. (You may probably want to add `1` before displaying it to an user.)" example: 0 502: $ref: "#/components/responses/RedisCmdFailed" 504: $ref: "#/components/responses/RedisConnFailed" put: operationId: "putScore" summary: "Submit a score to a board" tags: ["Score"] parameters: - $ref: "#/components/parameters/board" - $ref: "#/components/parameters/player" requestBody: required: true content: application/json: schema: type: number description: "The score to submit to the board." example: 1234.56 security: - XBoardToken: [] responses: 200: description: "Score discarded as it was worse than the previous one" content: application/json: schema: type: object properties: score: type: number description: "The score of the specified player." example: 1234.56 rank: type: integer description: "The zero-indexed rank of the specified player. (You may probably want to add `1` before displaying it to an user.)" example: 0 201: description: "Score submitted and updated" content: application/json: schema: type: object properties: score: type: number description: "The score of the specified player." example: 2468.13 rank: type: integer description: "The zero-indexed rank of the specified player. (You may probably want to add `1` before displaying it to an user.)" example: 0 401: description: "Missing, invalid or malformed Authorization header" content: application/json: schema: type: string example: "Missing Authorization header" 403: description: "Invalid board token" content: application/json: schema: type: string example: "Invalid board token" 502: $ref: "#/components/responses/RedisCmdFailed" 504: $ref: "#/components/responses/RedisConnFailed" components: securitySchemes: XCreateToken: type: http scheme: "bearer" bearerFormat: "setInEnvVars" XBoardToken: type: http scheme: "bearer" bearerFormat: "gVsuzIxgVfRx4RNl" parameters: board: name: "board" description: "The name of the board to operate on." in: query schema: type: string player: name: "player" description: "The name of the player to operate on." in: query schema: type: string offset: name: "offset" description: "The offset to start returning results from." in: query schema: type: integer size: name: "size" description: "How many results to return." in: query schema: type: integer minimum: 0 maximum: 500 responses: RedisCmdFailed: description: "Could not execute Redis command" content: application/json: schema: type: string example: "Could not execute Redis command" RedisConnFailed: description: "Could not connect to Redis" content: application/json: schema: type: string example: "Could not connect to Redis"