openapi: '3.0.3'
info:
  version: '1.0.0'
  title: 'Qomon Turf API'
  description: |-
    ## CRUD for Turfs and Layers
    This API give you facilities to record and retreive Turfs and Layers in the **Group** context of the logged-in **Users**.

    ### `Group context`
    The **group_id** is determined by the backend from the **selected_group_id**.

    ### `Turfs`
    A **Turf** is a geographic area freely define by end **Users** on the webapp.
    The aim is to draw lines that join somewhere on a map.
    The resulting free-form is an area which can be used to identify **Contacts** and carry out field **Actions**.

    A **Turf**:
    * is named,
    * hold a unique code that can be use for sharing/joining purpose, this code is randomly generated by the backend on creation,
    * hold the coordinates used to define an area,
    * can be assigned to **Users** and **Teams**.

    ### `Layer`
    A **Layer** is a container that group zero-to-many **Turfs** together.

    A **Layer**:
    * is named,
    * has a parent in the Qomon territory classification (define by data team).

servers:
  - url: https://integration.quorumapps.com
    description: Integration Backend API (uses test data)
  - url: https://test.quorumapps.com/api
    description: Integration through Webapp Proxy (uses test data)
  - url: https://production.qomon.app
    description: Production server (uses live data)

paths:
  /turf/layers:
    get:
      description: |-
        Retreive all **Layers** configured on the selected group of the logged-in user.

        When a **Layer** contain **Turfs**, they are also returned.
      tags:
        - layers
      operationId: getLayers
      responses:
        200:
          description: List of all **Layers** available for the selected group id
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/ResponseSuccess'
                  - type: object
                    required:
                      - data
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Layers'
              examples:
                getAllLayers:
                  $ref: '#/components/examples/GetAllLayersResponse'
        400:
          $ref: '#/components/responses/BadRequest'
        401:
          $ref: '#/components/responses/Unauthorized'
        403:
          $ref: '#/components/responses/Forbidden'
        404:
          $ref: '#/components/responses/NotFound'
        500:
          $ref: '#/components/responses/UnexpectedError'
    post:
      description: |-
        Create and return an empty **Layer**.

        Be aware that `name` and `parent` are required and `name` must be unique per group.
        **409 - Conflict** response code is returned when a **Layer** with same name already exist in the selected group.

        `parent` can be NULL. This means that the layer is linked to the highest level of the territory classification.
      tags:
        - layers
      operationId: postLayers
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - data
              properties:
                data:
                  $ref: '#/components/schemas/Layers'
            examples:
              postAnEmptyLayer:
                $ref: '#/components/examples/CreateEmptyLayerRequestBody'
      responses:
        200:
          description: |-
            Empty **Layer** successfully created
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/ResponseSuccess'
                  - type: object
                    required:
                      - data
                    properties:
                      data:
                        $ref: '#/components/schemas/Layers'
              examples:
                postAnEmptyLayer:
                  $ref: '#/components/examples/CreateEmptyLayerResponse'
        400:
          $ref: '#/components/responses/BadRequest'
        401:
          $ref: '#/components/responses/Unauthorized'
        403:
          $ref: '#/components/responses/Forbidden'
        404:
          $ref: '#/components/responses/NotFound'
        409:
          $ref: '#/components/responses/Conflict'
        500:
          $ref: '#/components/responses/UnexpectedError'
  /turf/layers/{layerId}:
    parameters:
      - name: layerId
        description: The unique id of the layer
        in: path
        required: true
        schema:
          $ref: '#/components/schemas/LayerID'
    get:
      description: |-
        Retreive the **Layer** identified by `layerId`.

        When the **Layer** contain **Turfs**, they are also returned.
      tags:
        - layers
      operationId: getLayerById
      responses:
        200:
          description: Layer identified by `layerId` successfully retreived
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/ResponseSuccess'
                  - type: object
                    required:
                      - data
                    properties:
                      data:
                        $ref: '#/components/schemas/Layers'
              examples:
                getLayerByID:
                  $ref: '#/components/examples/GetLayerByIDResponse'
        400:
          $ref: '#/components/responses/BadRequest'
        401:
          $ref: '#/components/responses/Unauthorized'
        403:
          $ref: '#/components/responses/Forbidden'
        404:
          $ref: '#/components/responses/NotFound'
        500:
          $ref: '#/components/responses/UnexpectedError'
    patch:
      description: |-
        Update a **Layer** identified by `layerId`.
        Each attribute can be updated separately except the `turfs` associative array.
        
        See [PATCH /turf/turfs/{turfId}](#/paths/turf/turfs/{turfId}) if you want to update **Turfs** attributes.

        In the same way as when creating a **Layer**, `name` must be unique per group.
        **409 - Conflict** response code is returned when a **Layer** with same name already exists in the selected group.
      tags:
        - layers
      operationId: patchLayerById
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - data
              properties:
                data:
                  $ref: '#/components/schemas/Layers'
            examples:
              patchLayerUpdateName:
                $ref: '#/components/examples/UpdateLayerNameRequestBody'
              patchLayerUpdateParent:
                $ref: '#/components/examples/UpdateLayerParentRequestBody'
      responses:
        200:
          description: Layer identified by `layerId` successfully updated
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/ResponseSuccess'
                  - type: object
                    required:
                      - data
                    properties:
                      data:
                        $ref: '#/components/schemas/Layers'
              examples:
                patchLayerUpdateName:
                  $ref: '#/components/examples/UpdateLayerNameResponse'
                patchLayerUpdateParent:
                  $ref: '#/components/examples/UpdateLayerParentResponse'
        400:
          $ref: '#/components/responses/BadRequest'
        401:
          $ref: '#/components/responses/Unauthorized'
        403:
          $ref: '#/components/responses/Forbidden'
        404:
          $ref: '#/components/responses/NotFound'
        409:
          $ref: '#/components/responses/Conflict'
        500:
          $ref: '#/components/responses/UnexpectedError'
    delete:
      description: |-
        Remove a **Layer**.

        What happens if **Turfs** are linked to the `layerId`?
        * _SOFT-DELETE_: (default) when the turf is associated with one-to-many **Actions**, it is archived (deleted_at),
        * _HARD-DELETE_: when the turf is not related to any **Actions**, it is permanently removed.
      tags:
        - layers
      operationId: deleteLayers
      responses:
        200:
          description: Layer identified by `layerId` successfully removed
        400:
          $ref: '#/components/responses/BadRequest'
        401:
          $ref: '#/components/responses/Unauthorized'
        403:
          $ref: '#/components/responses/Forbidden'
        404:
          $ref: '#/components/responses/NotFound'
        500:
          $ref: '#/components/responses/UnexpectedError'
  /turf/turfs:
    get:
      description: |-
        Retreive all **Turfs** configured on the selected group of the logged-in user
      tags:
        - turfs
      operationId: getTurfs
      responses:
        200:
          description: List of all **Turfs** available for the selected group id
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/ResponseSuccess'
                  - type: object
                    required:
                      - data
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Turfs'
              examples:
                getAllTurfs:
                  $ref: '#/components/examples/GetAllTurfsResponse'
        400:
          $ref: '#/components/responses/BadRequest'
        401:
          $ref: '#/components/responses/Unauthorized'
        403:
          $ref: '#/components/responses/Forbidden'
        404:
          $ref: '#/components/responses/NotFound'
        500:
          $ref: '#/components/responses/UnexpectedError'
    post:
      description: |-
        Create and return an empty **Turf** with a random `code` that can be used to share it.

        Be aware that `layer_id` is required but `name` is not.
        This means that no name-based deduplication will be performed by the backend.
      tags:
        - turfs
      operationId: postTurfs
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - data
              properties:
                data:
                  $ref: '#/components/schemas/Turfs'
            examples:
              postEmptyTurf:
                $ref: '#/components/examples/CreateEmptyTurfRequestBody'
      responses:
        200:
          description: Turf successfully created
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/ResponseSuccess'
                  - type: object
                    required:
                      - data
                    properties:
                      data:
                        $ref: '#/components/schemas/Turfs'
              examples:
                postAnEmptyLayer:
                  $ref: '#/components/examples/CreateEmptyTurfResponse'
        400:
          $ref: '#/components/responses/BadRequest'
        401:
          $ref: '#/components/responses/Unauthorized'
        403:
          $ref: '#/components/responses/Forbidden'
        404:
          $ref: '#/components/responses/NotFound'
        500:
          $ref: '#/components/responses/UnexpectedError'
  /turf/turfs/{turfId}:
    parameters:
      - name: turfId
        description: The unique code of the turf returned after creation
        in: path
        required: true
        schema:
          $ref: '#/components/schemas/TurfID'
    get:
      description: |-
        Retreive the **Turf** identified by `turfId`
      tags:
        - turfs
      operationId: getTurfById
      responses:
        200:
          description: Turf identified by `turfId` successfully retreived
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/ResponseSuccess'
                  - type: object
                    required:
                      - data
                    properties:
                      data:
                        $ref: '#/components/schemas/Turfs'
              examples:
                getLayerByID:
                  $ref: '#/components/examples/GetTurfByIDResponse'
        400:
          $ref: '#/components/responses/BadRequest'
        401:
          $ref: '#/components/responses/Unauthorized'
        403:
          $ref: '#/components/responses/Forbidden'
        404:
          $ref: '#/components/responses/NotFound'
        500:
          $ref: '#/components/responses/UnexpectedError'
    patch:
      description: |-
        Update a **Turf** identified by `turfId`.
        Each attribute can be updated separately except `code` which you are not allowed to update, as it is managed by the backend when the turf is created.

        **400 - Bad Request** will be returned if the request body contains a modified `code` value.

        When `coordinates`, `users` and `teams` arrays are set in the request body,
        the API replace them with the new version provided.

        If you don't want to update these arrays,
        it's easiest not to include the attribute in the request body.
      tags:
        - turfs
      operationId: patchTurfById
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - data
              properties:
                data:
                  $ref: '#/components/schemas/Turfs'
            examples:
              patchTurfName:
                $ref: '#/components/examples/UpdateTurfNameRequestBody'
              patchTurfUpdateUsers:
                $ref: '#/components/examples/UpdateTurfFullRequestBody'
      responses:
        200:
          description: Turf identified by `turfId` successfully updated into layer identified by `layerId`
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/ResponseSuccess'
                  - type: object
                    required:
                      - data
                    properties:
                      data:
                        $ref: '#/components/schemas/Turfs'
              examples:
                patchLayerUpdateName:
                  $ref: '#/components/examples/UpdateTurfNameResponse'
                patchLayerUpdateTurfs:
                  $ref: '#/components/examples/UpdateTurfFullResponse'
        400:
          $ref: '#/components/responses/BadRequest'
        401:
          $ref: '#/components/responses/Unauthorized'
        403:
          $ref: '#/components/responses/Forbidden'
        404:
          $ref: '#/components/responses/NotFound'
        500:
          $ref: '#/components/responses/UnexpectedError'
    delete:
      description: |-
        Remove a **Turf** in the backend.

        Deletion rules:
        * _SOFT-DELETE_: (default) when the turf is associated with one-to-many **Actions**, it is archived (deleted_at),
        * _HARD-DELETE_: when the turf is not related to any **Actions**, it is permanently removed.
      tags:
        - turfs
      operationId: deleteTurfs
      responses:
        200:
          description: Turf identified by `turfId` successfully removed
        400:
          $ref: '#/components/responses/BadRequest'
        401:
          $ref: '#/components/responses/Unauthorized'
        403:
          $ref: '#/components/responses/Forbidden'
        404:
          $ref: '#/components/responses/NotFound'
        500:
          $ref: '#/components/responses/UnexpectedError'

components:
  responses:
    BadRequest:
      description: Invalid request, such as lacking required request body or parameter
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Conflict:
      description: Duplicate resource
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: User authenticated but does not have permission to access the requested resource
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: The specified resource was not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: User must authenticate before using this api
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    UnexpectedError:
      description: An internal error occured
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  schemas:
    Coordinates:
      description: array of array of double precision numbers
      type: array
      items:
        $ref: '#/components/schemas/Points'
    LayerID:
      description: The unique identifier of the layer
      type: integer
    Layers:
      type: object
      required:
        - name
        - parent
      properties:
        id:
          type: integer
        name:
          type: string
        parent:
          type: string
          nullable: true
        turfs:
          $ref: '#/components/schemas/TurfsMap'
    Points:
      description: |-
        array of double precision numbers.
        First item is the latitude, second is the longitude
      type: array
      items:
        type: number
        format: double
      minItems: 2
      maxItems: 2
    TeamID:
      description: The unique identifier of a team
      type: integer
    TurfID:
      description: The unique identifier of the turf
      type: integer
    Turfs:
      description: TODO
      type: object
      required:
        - layer_id
      properties:
        id:
          $ref: '#/components/schemas/TurfID'
        code:
          description: Unique code of the turf that can be used to share it with mobile users
          type: string
          minLength: 6
          maxLength: 6
        layer_id:
          type: integer
        name:
          type: string
        coordinates:
          type: array
          items:
            $ref: '#/components/schemas/Coordinates'
        users:
          type: array
          items:
            $ref: '#/components/schemas/UserID'
        teams:
          type: array
          items:
            $ref: '#/components/schemas/TeamID'
    TurfsMap:
      description: The turfs assigned to this layer. Empty on creation.
      type: object
      additionalProperties:
        $ref: '#/components/schemas/Turfs'
    UserID:
      description: The unique identifier of a user
      type: integer
    Error:
      allOf:
        - $ref: '#/components/schemas/ResponseError'
        - type: object
          required:
            - message
          properties:
            message:
              description: A human readable error message usually considered presentable on user interfaces
              type: string
    ResponseError:
      type: object
      required:
        - status
      properties:
        status:
          type: string
          enum:
            - error
            - fail
    ResponseSuccess:
      type: object
      required:
        - status
      properties:
        status:
          type: string
          enum:
            - success
  securitySchemes:
    bearerAuth:
      description: User must send this token in the `Authorization` header
      type: http
      scheme: bearer
      bearerFormat: JWT
  examples:
    GetAllLayersResponse:
      summary: Get All Layers
      value:
        status: success
        data:
          - id: 1
            name: Layer 1
            parent: municipality
            turfs:
              XGJI34:
                id: 1
                code: XGJI34
                layer_id: 1
                name: Turf 1
                coordinates:
                  -
                    -
                      - 4.008320969298012
                      - 48.821865319079905
                    -
                      - 1.4061966108020272
                      - 47.39299598783589
                    -
                      - 3.157158795958253
                      - 47.06270545883166
                    -
                      - 4.008320969298012
                      - 48.821865319079905
                users: []
                teams:
                  - 385
              AK46D5:
                id: 2
                code: AK46D5
                layer_id: 1
                name: Turf 2
                coordinates:
                  -
                    -
                      - 6.732039923985525
                      - 48.323045050619896
                    -
                      - 4.008320969298012
                      - 48.821865319079905
                    -
                      - 3.157158795958253
                      - 47.06270545883166
                    -
                      - 6.732039923985525
                      - 48.323045050619896
                  -
                    -
                      - 4.51768603460053
                      - 48.304270494960036
                    -
                      - 4.343544963748656
                      - 48.007390925850586
                    -
                      - 5.001411231409151
                      - 48.17540349098178
                    -
                      - 4.51768603460053
                      - 48.304270494960036
                users:
                  - 1282
                teams:
                  - 354
          - id: 2
            name: Layer 2
            parent: null
            turfs:
              L4S2FR:
                id: 3
                code: L4S2FR
                layer_id: 2
                name: Turf 3
                coordinates:
                  -
                    -
                      - 1.4061966108020272
                      - 47.39299598783589
                    -
                      - 1.138688499180688
                      - 46.0932136053886
                    -
                      - 3.157158795958253
                      - 47.06270545883166
                    -
                      - 1.4061966108020272
                      - 47.39299598783589
                users:
                  - 269
                  - 3635
                teams: []
    CreateEmptyLayerRequestBody:
      summary: Create an empty layer
      value:
        data:
          name: New layer
          parent: municipality
    CreateEmptyLayerResponse:
      summary: Empty layer just created
      value:
        status: success
        data:
          id: 4
          name: New layer
          parent: municipality
          turfs: {}
    GetLayerByIDResponse:
      summary: Get Layer 1
      value:
        status: success
        data:
          id: 1
          name: Layer 1
          parent: municipality
          turfs:
            XGJI34:
              id: 1
              code: XGJI34
              layer_id: 1
              name: Turf 1
              coordinates:
                -
                  -
                    - 4.008320969298012
                    - 48.821865319079905
                  -
                    - 1.4061966108020272
                    - 47.39299598783589
                  -
                    - 3.157158795958253
                    - 47.06270545883166
                  -
                    - 4.008320969298012
                    - 48.821865319079905
              users: []
              teams:
                - 385
            AK46D5:
              id: 2
              code: AK46D5
              layer_id: 1
              name: Turf 2
              coordinates:
                -
                  -
                    - 6.732039923985525
                    - 48.323045050619896
                  -
                    - 4.008320969298012
                    - 48.821865319079905
                  -
                    - 3.157158795958253
                    - 47.06270545883166
                  -
                    - 6.732039923985525
                    - 48.323045050619896
                -
                  -
                    - 4.51768603460053
                    - 48.304270494960036
                  -
                    - 4.343544963748656
                    - 48.007390925850586
                  -
                    - 5.001411231409151
                    - 48.17540349098178
                  -
                    - 4.51768603460053
                    - 48.304270494960036
              users:
                - 1282
              teams:
                - 354
    UpdateLayerNameRequestBody:
      summary: Update layer 4 name
      value:
        data:
          name: New Layer name
    UpdateLayerNameResponse:
      summary: Layer name updated
      value:
        status: success
        data:
          id: 4
          name: New Layer name
          parent: municipality
          turfs: {}
    UpdateLayerParentRequestBody:
      summary: Update layer 4 parent
      value:
        data:
          parent: null
    UpdateLayerParentResponse:
      summary: Layer parent updated
      value:
        status: success
        data:
          id: 4
          name: New Layer name
          parent: null
          turfs: {}
    GetAllTurfsResponse:
      summary: Get all Turfs
      value:
        status: success
        data:
          - id: 1
            code: XGJI34
            layer_id: 1
            name: Turf 1
            coordinates:
              -
                -
                  - 4.008320969298012
                  - 48.821865319079905
                -
                  - 1.4061966108020272
                  - 47.39299598783589
                -
                  - 3.157158795958253
                  - 47.06270545883166
                -
                  - 4.008320969298012
                  - 48.821865319079905
            users: []
            teams:
              - 385
          - id: 2
            code: AK46D5
            layer_id: 1
            name: Turf 2
            coordinates:
              -
                -
                  - 6.732039923985525
                  - 48.323045050619896
                -
                  - 4.008320969298012
                  - 48.821865319079905
                -
                  - 3.157158795958253
                  - 47.06270545883166
                -
                  - 6.732039923985525
                  - 48.323045050619896
              -
                -
                  - 4.51768603460053
                  - 48.304270494960036
                -
                  - 4.343544963748656
                  - 48.007390925850586
                -
                  - 5.001411231409151
                  - 48.17540349098178
                -
                  - 4.51768603460053
                  - 48.304270494960036
            users:
              - 1282
            teams:
              - 354
          - id: 3
            code : L4S2FR
            layer_id: 2
            name: Turf 3
            coordinates:
              -
                -
                  - 1.4061966108020272
                  - 47.39299598783589
                -
                  - 1.138688499180688
                  - 46.0932136053886
                -
                  - 3.157158795958253
                  - 47.06270545883166
                -
                  - 1.4061966108020272
                  - 47.39299598783589
            users:
              - 269
              - 3635
            teams: []
    CreateEmptyTurfRequestBody:
      summary: Create an empty turf
      value:
        data:
          layer_id: 1
    CreateEmptyTurfResponse:
      summary: Turf just created
      value:
        status: success
        data:
          id: 4
          code: A5HJ9E
          layer_id: 1
          name: ""
          coordinates: []
          users: []
          teams: []
    GetTurfByIDResponse:
      summary: Turf with id 1
      value:
        status: success
        data:
          id: 1
          code: XGJI34
          layer_id: 1
          name: Turf 1
          coordinates:
            -
              -
                - 4.008320969298012
                - 48.821865319079905
              -
                - 1.4061966108020272
                - 47.39299598783589
              -
                - 3.157158795958253
                - 47.06270545883166
              -
                - 4.008320969298012
                - 48.821865319079905
          users: []
          teams:
            - 385
    UpdateTurfNameRequestBody:
      summary: Update a turf name
      value:
        data:
          name: Turf 3 renamed
    UpdateTurfNameResponse:
      summary: Turf name updated
      value:
        status: success
        data:
          id: 4
          code: A5HJ9E
          layer_id: 1
          name: Turf 3 renamed
          coordinates: []
          users: []
          teams: []
    UpdateTurfFullRequestBody:
      summary: Update all turf attributes
      value:
        data:
          id: 3
          layer_id: 3
          name: Turf 3 renamed
          coordinates:
            -
              -
                - 4.008320969298012
                - 48.821865319079905
              -
                - 1.4061966108020272
                - 47.39299598783589
              -
                - 3.157158795958253
                - 47.06270545883166
              -
                - 4.008320969298012
                - 48.821865319079905
          users:
            - 1234
          teams:
            - 5678
    UpdateTurfFullResponse:
      summary: Turf fully updated
      value:
        status: success
        data:
          id: 5
          code: A5HJ9E
          layer_id: 3
          name: Turf 3 renamed
          coordinates:
            -
              -
                - 4.008320969298012
                - 48.821865319079905
              -
                - 1.4061966108020272
                - 47.39299598783589
              -
                - 3.157158795958253
                - 47.06270545883166
              -
                - 4.008320969298012
                - 48.821865319079905
          users:
            - 1234
          teams:
            - 5678

security:
  - bearerAuth: []
