# https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md
openapi: '3.0.3'
info:
  version: '1.0.0'
  title: veloze-restbase
  description: |-
    Blueprint for OpenAPI specification
servers:
  - url: https://api.server.test/v1/docs
tags:
  - name: query
    description: query operation
  - name: single
    description: single operation
  - name: many
    description: bulk operation
paths:
  /:
    get:
      tags:
        - query
      summary: Find documents
      description: |-
        Find documents

        <a id="query-operators-numeric"> </a>

        ### Query Operators numeric

        For properties with numeric tye the following operators can be applied:

        | operator | description                                                         |
        | -------- | ------------------------------------------------------------------- |
        | $gt      | Matches values that are greater than a specified value.             |
        | $gte     | Matches values that are greater than or equal to a specified value. |
        | $lt      | Matches values that are less than a specified value.                |
        | $lte     | Matches values that are less than or equal to a specified value.    |
        | $ne      | Matches all values that are not equal to a specified value.         |

        **Examples**

        ```js
        // 10 < width <= 15
        GET ?width%24gt=10&width%24lte=15

        // height !== 17
        GET ?height%24ne=17
        ```

        <a id="query-operators-string"> </a>

        ### Query Operators string

        For properties with string tye the following operators can be applied:

        | operator | description                                            |
        | -------- | ------------------------------------------------------ |
        | $starts  | starts-with search                                     |
        | $like    | contains                                               |
        | $ends    | ends-with search                                       |
        | $cs      | (modifier) case sensitive search                       |
        | $not     | (modifier) inverse search e.g. `field$not$like=foobar` |      

        > **⚠️ NOTE:** Case (in-)sensitive search may not work for all database
        > adapters. Please consider setting the correct collation.

        **Examples**

        ```js
        // search all `item`s which do not contain `paper` case-insensitive
        GET ?item%24not%24like=paper

        // search all `article`s which starts-with `Jacket` case-sensitive
        GET ?article%24starts%24cs=Jacket
        ```

        <a id="query-parameters"> </a>

        | param     | type     | description                                                                                                                            |
        | --------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------- |
        | offset    | integer  | pagination offset                                                                                                                      |
        | limit     | integer  | pagination limit or size (defaults to 100 documents)                                                                                   |
        | countDocs | boolean  | Include document count in response.                                                                                                    |
        | fields    | string[] | comma separated list of schema properties which shall be returned.                                                                     |
        | sort      | string[] | comma separated list of schema properties for sorting. <br>Needs `$desc` operator for descending sort. <br>Defaults to ascending sort. |

        > **⚠️ NOTE:** Avoid using the params as document properties. You won't be able to
        > query for any of these doc properties then.

        **Examples**

        ```js
        // get the 2nd page for a page which contains 100 documents
        GET ?offset=100&limit=100&countDocs=true

        // get back only { id, v, item } properties from the query
        GET ?fields=id,v,item

        // sort result set by ascending `price` and descending `date`
        GET ?sort=price,date%24desc
        ```
      parameters:
        - name: id
          description: |-
            Find documents by id. <br>
            May be a string or a comma separated list of ids.
          in: query
          schema:
            type: string
          style: form
        # - name: property
        #   description: todo
        #   in: query
        #   schema:
        #     type: string
        - name: offset
          description: Pagination offset
          in: query
          schema:
            type: integer
            minimum: 0
          example: 0
        - name: limit
          description: |-
            Pagination limit

            if 0 then all documents are returned.
          in: query
          schema:
            type: integer
            minimum: 0
            default: 100
          example: 100
        - name: countDocs
          description: |-
            Include document count in response
          in: query
          schema:
            type: boolean
        - name: fields
          description: |-
            Comma separated list of schema properties which shall be returned
          in: query
          style: form
          schema:
            type: string
          example: id,v,createdAt
        - name: sort
          description: |-
            Comma separated list of schema properties for sorting.

            Needs `$desc` operator for descending sort. <br>
            Defaults to ascending sort.
          in: query
          style: form
          schema:
            type: string
          example: v$desc,name
      responses:
        '200':
          description: |-
            OK
          headers:
            x-request-id:
              description: correlation id of the request
              schema:
                type: string
          content:
            application/json:
              schema:
                type: object
                properties:
                  offset:
                    description: |- 
                      pagination offset
                    type: integer
                    minimum: 0
                  limit:
                    description: |- 
                      pagination limit
                    type: integer
                    minimum: 0
                    default: 100
                  count: 
                    description: |-
                      document count

                      requires `&countDocs=true`.
                    type: integer
                    minimum: 0
                    example: 42
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Document'
        '400': 
          description: |-
            Bad Content

            Schema check on provided query parameters failed.
          headers:
            x-request-id:
              description: correlation id of the request
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                reqId: cb993711-8395-4269-a2a8-f481fbeda243
                status: 400
                message: validation error
                errors:
                  foo: unsupported property
    post:
      tags:
        - single
      summary: Create new document
      description: |-
        Create a new document
      requestBody:
        description: |-
          Do not provide `id`, `version`, `createdAt` property. This is handled by the service.
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Document'
            example: {}
      responses:
        '201':
          description: Created
          headers:
            x-request-id:
              description: correlation id of the request
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Document'
        '400':
          description: |- 
            Bad Content

            Validation errors
          headers:
            x-request-id:
              description: correlation id of the request
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                reqId: 3b1123ca-43d6-45d2-95a0-f2f09ba778fc
                status: 400
                message: validation error
                errors:
                  quantity: must be integer
                  item: must have required property 'item'
    put:
      tags:
        - many
      summary: Update multiple documents
      description: |-
        Update multiple documents
      requestBody:
        content:
          application/json:
            schema:
              type: array
              items:
                maxItems: 10000
                $ref: '#/components/schemas/Document'
            examples:
              insert:
                summary: Update two documents
                value:
                  - {"id": "0lgg8pg824co0Z3dHVGDOqDm", "version": 1, "count": 3}
                  - {"id": "0lgg8s0tepN75w82I8DyoyII", "version": 5, "item":"scissor" }
      responses:
        '200':
          description: |-
            OK

            The status code is always 200.

            In case that a document can't be created then an error is returned
            for that position in the response array. 

            Such errors will NOT contain an `id` field but contain a `status`
            field with the error code.
          headers:
            x-request-id:
              description: correlation id of the request
              schema:
                type: string
          content:
            application/json:
              schema:
                type: array
                items: 
                  anyOf:
                    - $ref: '#/components/schemas/Document'
                    - type: object
                      properties:
                        status:
                          type: integer
                          example: 400
                        message: 
                          type: string
                          example: validation error
                        errors:
                          type: object
                          example:
                            foo: unsupported property
  /{id}:
    get:
      tags:
        - single
      summary: Get document by id
      description: |-
        Get a document by id
      parameters:
        - name: id
          description: document id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          headers:
            x-request-id:
              description: correlation id of the request
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Document'
        '404':
          description: Not Found
          headers:
            x-request-id:
              description: correlation id of the request
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    put:
      tags:
        - single
      summary: Update document by id
      description: |-
        Update document by id
      parameters:
        - name: id
          description: document id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        description: |-
          The current `v` version property must be provided. Otherwise
          Optimistic Locking will always fail.

          Provide all "required" parameters.
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Document'
            example:
              id: 0lgg8pg824co0Z3dHVGDOqDm
              v: 2
      responses:
        '200':
          description: |- 
            OK

            the updated document is returned.
          headers:
            x-request-id:
              description: correlation id of the request
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Document'
        '400':
          description: |- 
            Bad Content

            Validation errors
          headers:
            x-request-id:
              description: correlation id of the request
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                reqId: 3b1123ca-43d6-45d2-95a0-f2f09ba778fc
                status: 400
                message: validation error
                errors:
                  quantity: must be integer
                  item: must have required property 'item'
        '404':
          description: Not Found
          headers:
            x-request-id:
              description: correlation id of the request
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: |-
            Conflict

            Document could not be updated. The document might have been updated
            in the meantime.

            Fetch the document with GET /{id} and resolve
            the conflict before updating again,
          headers:
            x-request-id:
              description: correlation id of the request
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                reqId: 3b1123ca-43d6-45d2-95a0-f2f09ba778fc
                status: 409
                message: Conflict
    delete:
      tags:
        - single
      summary: Delete document by id
      description: |-
        Delete a document by document id
      parameters:
        - name: id
          description: document id
          in: path
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
          headers:
            x-request-id:
              description: correlation id of the request
              schema:
                type: string
        '404':
          description: Not Found
          headers:
            x-request-id:
              description: correlation id of the request
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /search:
    post:
      tags:
        - query
      summary: Query multiple documents
      description: |-
        Query multiple documents.

        You may also use `SEARCH /` as alternative method

        Only properties which are named in the JSON-schema of the model can be used as
        search parameters. 

        All Query params from [GET /{modelName}&nbsp;](#get-modelname) can be used to
        form complex search terms.

        * [Query Operators numeric](#query-operators-numeric)
        * [Query Operators string](#query-operators-string)
        * [Query Parameters](#query-parameters)

        Additionally the boolean operators `$and` and `$or` can be used. 

        E.g. with the following JSON-Schema

        ```json
        {
          "type": "object",
          "properties": {
            "item": { "type": "string" },
            "width": { "type": "integer" } 
          }
        }
        ```
      requestBody:
        description: |-
          Search Request
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Query'
            examples:
              firstTenDocs:
                summary: Find by not item name and width
                description: |-
                  To search for the first 10 documents which do not contain
                  "paper" (case-insensitive) and have a width greater 5 and less
                  than 30, sorted by "item" and "width" (descending), where only
                  the fields "id", "item" and "width" are returned.
                value:
                  offset: 0
                  limit: 10
                  sort:
                    - item: 1
                    - width: -1
                  fields:
                    - id
                    - item
                    - width
                  $and:
                    - item:
                        $like: paper
                        $not: true
                        $cs: false
                    - width:
                        $gt: 5
                        $lte: 30
              itemByNames:
                summary: Find item by names
                description: |-
                  To obtain all documents where item is "paper", "journal"
                  sorted by "width".
                value:
                  fields: 
                    - id
                    - width
                  sort:
                    - width: 1
                  item: 
                    - paper
                    - journal
      responses:
        '200':
          description: |-
            OK
          headers:
            x-request-id:
              description: correlation id of the request
              schema:
                type: string
          content:
            application/json:
              schema:
                type: object
                properties:
                  offset:
                    description: |- 
                      pagination offset
                    type: integer
                    minimum: 0
                  limit:
                    description: |- 
                      pagination limit
                    type: integer
                    minimum: 0
                    default: 100
                  count: 
                    description: |-
                      document count

                      requires `&countDocs=true`.
                    type: integer
                    minimum: 0
                    example: 42
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Document'
        '400': 
          description: |-
            Bad Content

            Schema check on provided query parameters failed.
          headers:
            x-request-id:
              description: correlation id of the request
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                reqId: cb993711-8395-4269-a2a8-f481fbeda243
                status: 400
                message: validation error
                errors:
                  foo: unsupported property
  /create:
    post:
      tags:
        - many
      summary: Create multiple documents
      description: |-
        Create multiple documents
      requestBody:
        content:
          application/json:
            schema:
              type: array
              items:
                maxItems: 10000
                $ref: '#/components/schemas/Document'
            examples:
              insert:
                summary: Insert two documents
                value:
                  - {"item":"paper","count":2}
                  - {"item":"scissor","count":7}
          # application/x-ndjson:
          #   schema:
          #     type: string
          #   examples:
          #     insert:
          #       summary: Insert two documents
          #       description: |-
          #         ````
          #         {"item":"paper","count":2}
          #         {"item":"scissor","count":7}
          #         ````
          #       value: ""
      responses:
        '201':
          description: |-
            Created

            The status code is always 201.

            In case that a document can't be created then an error is returned
            for that position in the response array. 

            Such errors will NOT contain an `id` field but contain a `status`
            field with the error code.
          headers:
            x-request-id:
              description: correlation id of the request
              schema:
                type: string
          content:
            application/json:
              schema:
                type: array
                items: 
                  anyOf:
                    - $ref: '#/components/schemas/Document'
                    - type: object
                      properties:
                        status:
                          type: integer
                          example: 400
                        message: 
                          type: string
                          example: validation error
                        errors:
                          type: object
                          example:
                            foo: unsupported property
  /delete:
    post:
      tags:
        - many
      summary: Delete multiple documents
      description: |-
        Delete multiple documents by id
      requestBody:
        # description: 
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Query'
            examples:
              deleteItemsById:
                summary: Delete documents by id
                description: |-
                  Delete several documents by id
                value:
                  id: 
                    - 0lghmuf7i4saAnivZRjd7Zx2
                    - 0lghmufr3BtmVdblcIxWywnR
                    - 0lghmug97FftrdLlyMbcoPX5
                    - 0lghmugq6Efpj2Lul2gLlj26
              deleteNotPaper:
                summary: Find by not item name and width
                description: |-
                  Delete all documents which do not contain
                  "paper" (case-insensitive) and have a width greater 5 and less
                  than 30.
                value:
                  $and:
                    - item:
                        $like: paper
                        $not: true
                        $cs: false
                    - width:
                        $gt: 5
                        $lte: 30
      responses:
        '200':
          description: |-
            OK
          headers:
            x-request-id:
              description: correlation id of the request
              schema:
                type: string
          content:
            application/json:
              schema:
                type: object
                properties:
                  deletedCount: 
                    description: |-
                      Deleted documents count
                    type: integer
                    minimum: 0
                    example: 42
                  # data:
                  #   type: array
                  #   items:
                  #     $ref: '#/components/schemas/Document'
        '400': 
          description: |-
            Bad Content

            Schema check on provided query parameters failed.
          headers:
            x-request-id:
              description: correlation id of the request
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                reqId: cb993711-8395-4269-a2a8-f481fbeda243
                status: 400
                message: validation error
                errors:
                  foo: unsupported property

components:
  schemas:
    Document:
      description: schema definition
      type: object
      properties:
        id:
          description: document id
          type: string
          example: 0lgg8pg824co0Z3dHVGDOqDm
        v:
          description: document version
          type: integer
          example: 2
        createdAt:
          description: document creation timestamp
          type: string
          format: date-time
          example: 2023-04-14T07:40:46.562Z
        updatedAt:
          description: document update timestamp
          type: string
          format: date-time
          example: 2023-04-16T07:40:46.562Z
    Query:
      description: search query
      type: object
      anyOf:
        - $ref: '#/components/x-defs/and'
        - $ref: '#/components/x-defs/or'
        - $ref: '#/components/x-defs/findOptions'
        - $ref: '#/components/schemas/Document'
    Error:
      type: object
      required:
        - id
        - status
        - message
      properties:
        reqId:
          description: correlation id from request
          type: string
        status:
          description: response status code
          type: integer
          minimum: 400
          maximum: 504
        message:
          description: error message
          type: string
        errors:
          description: validation error info
          type: object
      example:
        reqId: 10752dee-0144-4632-8b8b-164bb1bce0aa
        status: 404
        message: Not Found
  x-defs:
    and:
      description: AND rule
      type: object
      required: 
        - "$and":
      properties:
        $and: 
          type: array 
          items:
            anyOf:
              - $ref: '#/components/schemas/Document'
              - $ref: '#/components/x-defs/or'
    or: 
      description: OR rule
      type: object
      required: 
        - "$or":
      properties:
        $or: 
          type: array 
          items:
            anyOf:
              - $ref: '#/components/schemas/Document'
              - $ref: '#/components/x-defs/and'
    numberOps:
      description: number operators
      type: object
      additionalProperties: false,
      properties:
        $gt:
          type: number
        $gte:
          type: number
        $lt:
          type: number
        $lte:
          type: number
        $ne:
          type: number
        $eq:
          type: number
    stringOps:
      description: string operators
      type: object
      additionalProperties: false
      properties:
        $like: 
          type: string
        $starts: 
          type: string
        $ends: 
          type: string
        $eq: 
          type: string
        $not: 
          anyOf:
            - type: string
            - type: boolean
        $cs: 
          anyOf:
            - type: string
            - type: boolean
    findOptions:
      description: find options
      type: object
      properties:
        offset:
          type: integer
          minimum: 0
        limit:
          type: integer
          exclusiveMinimum: -1
        countDocs:
          type: boolean
        sort:
          type: array
          items:
            type: object
            additionalProperties: false
        fields:
          description: Any field attributed in Document
          type: array
          items:
            type: string
            enum: []