# Copyright (c) Abstract Machines
# SPDX-License-Identifier: Apache-2.0

openapi: 3.0.3
info:
  title: SuperMQ Channels Service
  description: |
    This is the Channels Server based on the OpenAPI 3.0 specification.  It is the HTTP API for managing platform channels. You can now help us improve the API whether it's by making changes to the definition itself or to the code.
    Some useful links:
    - [The SuperMQ repository](https://github.com/absmach/supermq)
  contact:
    email: info@abstractmachines.fr
  license:
    name: Apache 2.0
    url: https://github.com/absmach/supermq/blob/main/LICENSE
  version: 0.18.0

servers:
  - url: http://localhost:9005
  - url: https://localhost:9005

tags:
  - name: Channels
    description: CRUD operations for your channels
    externalDocs:
      description: Find out more about channels
      url: https://docs.supermq.abstractmachines.fr/
  - name: Connections
    description: All operations involving channel and client connections
    externalDocs:
      description: Find out more about channel and client connections
      url: https://docs.supermq.abstractmachines.fr/
  - name: Health
    description: Health check operations
    externalDocs:
      description: Find out more about health checks
      url: https://docs.supermq.abstractmachines.fr/

paths:
  /{domainID}/channels:
    post:
      operationId: createChannel
      tags:
        - Channels
      summary: Creates new channel
      description: |
        Creates new channel in domain.
      requestBody:
        $ref: "#/components/requestBodies/ChannelCreateReq"
      security:
        - bearerAuth: []
      parameters:
        - $ref: "auth.yaml#/components/parameters/DomainID"
      responses:
        "201":
          $ref: "#/components/responses/ChannelCreateRes"
        "400":
          description: Failed due to malformed JSON.
        "401":
          description: Missing or invalid access token provided.
        "403":
          description: Failed to perform authorization over the entity.
        "404":
          description: A non-existent entity request.
        "409":
          description: Failed due to using an existing identity.
        "415":
          description: Missing or invalid content type.
        "422":
          description: Database can't process request.
        "500":
          $ref: "#/components/responses/ServiceError"

    get:
      operationId: listChannels
      summary: Lists channels.
      description: |
        Retrieves a list of channels. Due to performance concerns, data
        is retrieved in subsets. The API clients must ensure that the entire
        dataset is consumed either by making subsequent requests, or by
        increasing the subset size of the initial request.
      tags:
        - Channels
      security:
        - bearerAuth: []
      parameters:
        - $ref: "auth.yaml#/components/parameters/DomainID"
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Offset"
        - $ref: "#/components/parameters/Metadata"
        - $ref: "#/components/parameters/ChannelName"
      responses:
        "200":
          $ref: "#/components/responses/ChannelPageRes"
        "400":
          description: Failed due to malformed query parameters.
        "401":
          description: Missing or invalid access token provided.
        "403":
          description: Failed to perform authorization over the entity.
        "404":
          description: Channel does not exist.
        "422":
          description: Database can't process request.
        "500":
          $ref: "#/components/responses/ServiceError"

  /{domainID}/channels/bulk:
    post:
      operationId: createChannels
      tags:
        - Channels
      summary: Creates new channels
      description: |
        Creates new channels in domain.
      requestBody:
        $ref: "#/components/requestBodies/ChannelsCreateReq"
      security:
        - bearerAuth: []
      parameters:
        - $ref: "auth.yaml#/components/parameters/DomainID"
      responses:
        "201":
          $ref: "#/components/responses/ChannelsCreateRes"
        "400":
          description: Failed due to malformed JSON.
        "401":
          description: Missing or invalid access token provided.
        "403":
          description: Failed to perform authorization over the entity.
        "404":
          description: A non-existent entity request.
        "409":
          description: Failed due to using an existing identity.
        "415":
          description: Missing or invalid content type.
        "422":
          description: Database can't process request.
        "500":
          $ref: "#/components/responses/ServiceError"

  /{domainID}/channels/{chanID}:
    get:
      operationId: getChannel
      summary: Retrieves channel info.
      description: |
        Gets info on a channel specified by id.
      tags:
        - Channels
      parameters:
        - $ref: "auth.yaml#/components/parameters/DomainID"
        - $ref: "#/components/parameters/chanID"
      security:
        - bearerAuth: []
      responses:
        "200":
          $ref: "#/components/responses/ChannelRes"
        "400":
          description: Failed due to malformed channel's or domain ID.
        "401":
          description: Missing or invalid access token provided.
        "403":
          description: Failed to perform authorization over the entity.
        "404":
          description: Channel does not exist.
        "422":
          description: Database can't process request.
        "500":
          $ref: "#/components/responses/ServiceError"

    patch:
      operationId: updateChannel
      summary: Updates channel data.
      description: |
        Update is performed by replacing the current resource data with values
        provided in a request payload. Note that the channel's ID will not be
        affected.
      tags:
        - Channels
      parameters:
        - $ref: "auth.yaml#/components/parameters/DomainID"
        - $ref: "#/components/parameters/chanID"
      security:
        - bearerAuth: []
      requestBody:
        $ref: "#/components/requestBodies/ChannelUpdateReq"
      responses:
        "200":
          $ref: "#/components/responses/ChannelRes"
        "400":
          description: Failed due to malformed query parameters.
        "401":
          description: Missing or invalid access token provided.
        "403":
          description: Failed to perform authorization over the entity.
        "404":
          description: Channel does not exist.
        "409":
          description: Failed due to using an existing identity.
        "415":
          description: Missing or invalid content type.
        "422":
          description: Database can't process request.
        "500":
          $ref: "#/components/responses/ServiceError"
    delete:
      summary: Delete channel for given channel id.
      description: |
        Delete channel remove given channel id from repo
        and removes all the policies related to channel.
      tags:
        - Channels
      parameters:
        - $ref: "auth.yaml#/components/parameters/DomainID"
        - $ref: "#/components/parameters/chanID"
      security:
        - bearerAuth: []
      responses:
        "204":
          description: Channel deleted.
        "400":
          description: Failed due to malformed domain ID.
        "401":
          description: Missing or invalid access token provided.
        "403":
          description: Unauthorized access to client id.
        "404":
          description: A non-existent entity request.
        "500":
          $ref: "#/components/responses/ServiceError"

  /{domainID}/channels/{chanID}/tags:
    patch:
      operationId: updateChannelTags
      summary: Updates channel tags.
      description: |
        Update is performed by replacing the current resource data with values
        provided in a request payload. Note that the channel's ID will not be
        affected.
      tags:
        - Channels
      parameters:
        - $ref: "auth.yaml#/components/parameters/DomainID"
        - $ref: "#/components/parameters/chanID"
      security:
        - bearerAuth: []
      requestBody:
        $ref: "#/components/requestBodies/ChannelUpdateTagsReq"
      responses:
        "200":
          $ref: "#/components/responses/ChannelRes"
        "400":
          description: Failed due to malformed query parameters.
        "401":
          description: Missing or invalid access token provided.
        "403":
          description: Failed to perform authorization over the entity.
        "404":
          description: Channel does not exist.
        "409":
          description: Failed due to using an existing identity.
        "415":
          description: Missing or invalid content type.
        "422":
          description: Database can't process request.
        "500":
          $ref: "#/components/responses/ServiceError"

  /{domainID}/channels/{chanID}/enable:
    post:
      operationId: enableChannel
      summary: Enables a channel
      description: |
        Enables a specific channel that is identified by the channel ID.
      tags:
        - Channels
      parameters:
        - $ref: "auth.yaml#/components/parameters/DomainID"
        - $ref: "#/components/parameters/chanID"
      security:
        - bearerAuth: []
      responses:
        "200":
          $ref: "#/components/responses/ChannelRes"
        "400":
          description: Failed due to malformed query parameters.
        "401":
          description: Missing or invalid access token provided.
        "403":
          description: Failed to perform authorization over the entity.
        "404":
          description: A non-existent entity request.
        "409":
          description: Failed due to already enabled channel.
        "422":
          description: Database can't process request.
        "500":
          $ref: "#/components/responses/ServiceError"

  /{domainID}/channels/{chanID}/disable:
    post:
      operationId: disableChannel
      summary: Disables a channel
      description: |
        Disables a specific channel that is identified by the channel ID.
      tags:
        - Channels
      parameters:
        - $ref: "auth.yaml#/components/parameters/DomainID"
        - $ref: "#/components/parameters/chanID"
      security:
        - bearerAuth: []
      responses:
        "200":
          $ref: "#/components/responses/ChannelRes"
        "400":
          description: Failed due to malformed channel's ID.
        "401":
          description: Missing or invalid access token provided.
        "403":
          description: Failed to perform authorization over the entity.
        "404":
          description: A non-existent entity request.
        "409":
          description: Failed due to already disabled channel.
        "422":
          description: Database can't process request.
        "500":
          $ref: "#/components/responses/ServiceError"

  /{domainID}/channels/{chanID}/parent:
    post:
      operationId: setChannelParentGroup
      summary: Sets a parent group for a channel
      description: |
        Sets a parent group for a specific channel that is identified by the channel ID.
      tags:
        - Channels
      parameters:
        - $ref: "auth.yaml#/components/parameters/DomainID"
        - $ref: "#/components/parameters/chanID"
      requestBody:
        $ref: "#/components/requestBodies/ChannelParentGroupReq"
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Parent group set.
        "400":
          description: Failed due to malformed client's ID.
        "401":
          description: Missing or invalid access token provided.
        "403":
          description: Failed to perform authorization over the entity.
        "404":
          description: A non-existent entity request.
        "422":
          description: Database can't process request.
        "500":
          $ref: "#/components/responses/ServiceError"

    delete:
      operationId: removeChannelParentGroup
      summary: Removes a parent group from a channel.
      description: |
        Removes a parent group from a specific channel that is identified by the channel ID.
      tags:
        - Channels
      parameters:
        - $ref: "auth.yaml#/components/parameters/DomainID"
        - $ref: "#/components/parameters/chanID"
      requestBody:
        $ref: "#/components/requestBodies/ChannelParentGroupReq"
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Parent group removed.
        "400":
          description: Failed due to malformed client's ID.
        "401":
          description: Missing or invalid access token provided.
        "403":
          description: Failed to perform authorization over the entity.
        "404":
          description: A non-existent entity request.
        "422":
          description: Database can't process request.
        "500":
          $ref: "#/components/responses/ServiceError"

  /{domainID}/channels/connect:
    post:
      operationId: connectClientsAndChannels
      summary: Connects client and channel.
      description: |
        Connect clients specified by IDs to channels specified by IDs.
        Channel and client are owned by user identified using the provided access token.
      parameters:
        - $ref: "auth.yaml#/components/parameters/DomainID"
      tags:
        - Connections
      requestBody:
        $ref: "#/components/requestBodies/ConnReq"
      responses:
        "201":
          description: Connection created.
        "400":
          description: Failed due to malformed JSON.
        "401":
          description: Missing or invalid access token provided.
        "403":
          description: Failed to perform authorization over the entity.
        "404":
          description: A non-existent entity request.
        "409":
          description: Entity already exist.
        "415":
          description: Missing or invalid content type.
        "422":
          description: Database can't process request.
        "500":
          $ref: "#/components/responses/ServiceError"

  /{domainID}/channels/disconnect:
    post:
      operationId: disconnectClientsAndChannels
      summary: Disconnects client and channel.
      description: |
        Disconnect clients specified by IDs from channels specified by IDs.
        Channel and client are owned by user identified using the provided access token.
      parameters:
        - $ref: "auth.yaml#/components/parameters/DomainID"
      tags:
        - Connections
      requestBody:
        $ref: "#/components/requestBodies/ConnReq"
      responses:
        "201":
          description: Connection removed.
        "400":
          description: Failed due to malformed JSON.
        "401":
          description: Missing or invalid access token provided.
        "403":
          description: Failed to perform authorization over the entity.
        "404":
          description: A non-existent entity request.
        "409":
          description: Entity already exist.
        "415":
          description: Missing or invalid content type.
        "422":
          description: Database can't process request.
        "500":
          $ref: "#/components/responses/ServiceError"

  /{domainID}/channels/{chanID}/connect:
    post:
      operationId: connectClientsToChannel
      summary: Connects clients to a channel
      description: |
        Connects clients to a channel that is identified by the channel ID.
      tags:
        - Connections
      parameters:
        - $ref: "auth.yaml#/components/parameters/DomainID"
        - $ref: "#/components/parameters/chanID"
      requestBody:
        $ref: "#/components/requestBodies/ChannelConnReq"
      responses:
        "200":
          description: Clients connected.
        "400":
          description: Failed due to malformed client's ID.
        "401":
          description: Missing or invalid access token provided.
        "403":
          description: Failed to perform authorization over the entity.
        "404":
          description: A non-existent entity request.
        "422":
          description: Database can't process request.
        "500":
          $ref: "#/components/responses/ServiceError"

  /{domainID}/channels/{chanID}/disconnect:
    post:
      operationId: disconnectClientsFromChannel
      summary: Disconnects clients from a channel
      description: |
        Disconnects clients to a channel that is identified by the channel ID.
      tags:
        - Connections
      parameters:
        - $ref: "auth.yaml#/components/parameters/DomainID"
        - $ref: "#/components/parameters/chanID"
      requestBody:
        $ref: "#/components/requestBodies/ChannelConnReq"
      responses:
        "204":
          description: Clients disconnected.
        "400":
          description: Failed due to malformed client's ID.
        "401":
          description: Missing or invalid access token provided.
        "403":
          description: Failed to perform authorization over the entity.
        "404":
          description: A non-existent entity request.
        "422":
          description: Database can't process request.
        "500":
          $ref: "#/components/responses/ServiceError"

  /health:
    get:
      summary: Retrieves service health check info.
      tags:
        - Health
      security: []
      responses:
        "200":
          $ref: "#/components/responses/HealthRes"
        "500":
          $ref: "#/components/responses/ServiceError"

components:
  schemas:
    ChannelReqObj:
      type: object
      properties:
        name:
          type: string
          example: channelName
          description: Free-form channel name. Channel name is unique on the given hierarchy level.
        parent_id:
          type: string
          example: bb7edb32-2eac-4aad-aebe-ed96fe073879
          description: Id of parent channel, it must be existing channel.
        route:
          type: string
          example: channelRoute
          description: Channel route.
        metadata:
          type: object
          example: { "location": "example" }
          description: Arbitrary, object-encoded channels's data.
        status:
          type: string
          description: Channel Status
          format: string
          example: enabled
      required:
        - name

    ParentGroupReqObj:
      type: object
      properties:
        parent_group_id:
          type: string
          format: uuid
          example: bb7edb32-2eac-4aad-aebe-ed96fe073879
          description: Parent group unique identifier.
      required:
        - parent_group_id

    Channel:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: bb7edb32-2eac-4aad-aebe-ed96fe073879
          description: Unique channel identifier generated by the service.
        name:
          type: string
          example: channelName
          description: Free-form channel name. Channel name is unique on the given hierarchy level.
        domain_id:
          type: string
          format: uuid
          example: bb7edb32-2eac-4aad-aebe-ed96fe073879
          description: ID of the domain to which the group belongs.
        parent_id:
          type: string
          format: uuid
          example: bb7edb32-2eac-4aad-aebe-ed96fe073879
          description: Channel parent identifier.
        route:
          type: string
          example: channelRoute
          description: Channel route.
        metadata:
          type: object
          example: { "role": "general" }
          description: Arbitrary, object-encoded channels's data.
        path:
          type: string
          example: bb7edb32-2eac-4aad-aebe-ed96fe073879.bb7edb32-2eac-4aad-aebe-ed96fe073879
          description: Hierarchy path, concatenated ids of channel ancestors.
        level:
          type: integer
          description: Level in hierarchy, distance from the root channel.
          format: int32
          example: 2
          maximum: 5
        created_at:
          type: string
          format: date-time
          example: "2019-11-26 13:31:52"
          description: Datetime when the channel was created.
        updated_at:
          type: string
          format: date-time
          example: "2019-11-26 13:31:52"
          description: Datetime when the channel was created.
        status:
          type: string
          description: Channel Status
          format: string
          example: enabled
      xml:
        name: channe

    ChannelsPage:
      type: object
      properties:
        channels:
          type: array
          minItems: 0
          uniqueItems: true
          items:
            $ref: "#/components/schemas/Channel"
        total:
          type: integer
          example: 1
          description: Total number of items.
        offset:
          type: integer
          description: Number of items to skip during retrieval.
        limit:
          type: integer
          example: 10
          description: Maximum number of items to return in one page.
      required:
        - channels
        - total
        - offset

    ChannelUpdate:
      type: object
      properties:
        name:
          type: string
          example: channelName
          description: Free-form channel name. Channel name is unique on the given hierarchy level.
        metadata:
          type: object
          example: { "role": "general" }
          description: Arbitrary, object-encoded channels's data.
      required:
        - name
        - metadata

    ChannelUpdateTags:
      type: object
      properties:
        tags:
          type: array
          minItems: 0
          items:
            type: string
          example: ["tag1", "tag2"]
          description: Channel tags.
      required:
        - tags

    ConnectionReqSchema:
      type: object
      properties:
        channel_ids:
          type: array
          description: Channel IDs.
          items:
            example: bb7edb32-2eac-4aad-aebe-ed96fe073879
        client_ids:
          type: array
          description: Client IDs
          items:
            example: bb7edb32-2eac-4aad-aebe-ed96fe073879
        types:
          type: array
          description: Connection types.
          items:
            example: publish

    ChannelConnectionReqSchema:
      type: object
      properties:
        client_ids:
          type: array
          description: Client IDs
          items:
            example: bb7edb32-2eac-4aad-aebe-ed96fe073879
        types:
          type: array
          description: Connection types.
          items:
            example: publish

    Error:
      type: object
      properties:
        error:
          type: string
          description: Error message
      example: { "error": "malformed entity specification" }

    HealthRes:
      type: object
      properties:
        status:
          type: string
          description: Service status.
          enum:
            - pass
        version:
          type: string
          description: Service version.
          example: 0.14.0
        commit:
          type: string
          description: Service commit hash.
          example: 7d6f4dc4f7f0c1fa3dc24eddfb18bb5073ff4f62
        description:
          type: string
          description: Service description.
          example: clients service
        build_time:
          type: string
          description: Service build time.
          example: 1970-01-01_00:00:00

  parameters:
    ClientName:
      name: name
      description: Client's name.
      in: query
      schema:
        type: string
      required: false
      example: "clientName"

    Status:
      name: status
      description: Client account status.
      in: query
      schema:
        type: string
        default: enabled
      required: false
      example: enabled

    Tags:
      name: tags
      description: Client tags.
      in: query
      schema:
        type: array
        minItems: 0
        uniqueItems: true
        items:
          type: string
      required: false
      example: ["yello", "orange"]

    ChannelName:
      name: name
      description: Channel's name.
      in: query
      schema:
        type: string
      required: false
      example: "channelName"

    ChannelDescription:
      name: name
      description: Channel's description.
      in: query
      schema:
        type: string
      required: false
      example: "channel description"

    chanID:
      name: chanID
      description: Unique channel identifier.
      in: path
      schema:
        type: string
        format: uuid
      required: true
      example: bb7edb32-2eac-4aad-aebe-ed96fe073879

    ParentId:
      name: parentId
      description: Unique parent identifier for a channel.
      in: query
      schema:
        type: string
        format: uuid
      required: false
      example: bb7edb32-2eac-4aad-aebe-ed96fe073879

    Metadata:
      name: metadata
      description: Metadata filter. Filtering is performed matching the parameter with metadata on top level. Parameter is json.
      in: query
      schema:
        type: string
        minimum: 0
      required: false

    Limit:
      name: limit
      description: Size of the subset to retrieve.
      in: query
      schema:
        type: integer
        default: 10
        maximum: 100
        minimum: 1
      required: false
      example: "100"

    Offset:
      name: offset
      description: Number of items to skip during retrieval.
      in: query
      schema:
        type: integer
        default: 0
        minimum: 0
      required: false
      example: "0"

    Connected:
      name: connected
      description: Connection state of the subset to retrieve.
      in: query
      schema:
        type: boolean
        default: true
      required: false

  requestBodies:
    ChannelCreateReq:
      description: JSON-formatted document describing the new channel to be registered
      required: true
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ChannelReqObj"

    ChannelsCreateReq:
      description: JSON-formatted document describing the new channels to be registered
      required: true
      content:
        application/json:
          schema:
            type: array
            items:
              $ref: "#/components/schemas/ChannelReqObj"

    ChannelUpdateReq:
      description: JSON-formated document describing the metadata and name of channel to be updated.
      required: true
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ChannelUpdate"

    ChannelUpdateTagsReq:
      description: JSON-formated document describing the tags of channel to be updated.
      required: true
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ChannelUpdateTags"

    ChannelParentGroupReq:
      description: JSON-formated document describing the parent group to be set to or removed from a channel.
      required: true
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ParentGroupReqObj"

    ConnReq:
      description: JSON-formatted document describing the new connection.
      required: true
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ConnectionReqSchema"

    ChannelConnReq:
      description: JSON-formatted document describing the new connection.
      required: true
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ChannelConnectionReqSchema"

  responses:
    ChannelCreateRes:
      description: Registered new channel.
      headers:
        Location:
          schema:
            type: string
            format: url
          description: Registered channel relative URL in the format `/channels/<channel_id>`
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Channel"
      links:
        get:
          operationId: getChannel
          parameters:
            chanID: $response.body#/id
        update:
          operationId: updateChannel
          parameters:
            chanID: $response.body#/id
        disable:
          operationId: disableChannel
          parameters:
            chanID: $response.body#/id
        enable:
          operationId: enableChannel
          parameters:
            chanID: $response.body#/id

    ChannelsCreateRes:
      description: Registered new channels.
      headers:
        Location:
          schema:
            type: string
            format: url
          description: Registered channel relative URL in the format `/channels/<channel_id>`
      content:
        application/json:
          schema:
            type: array
            items:
              $ref: "#/components/schemas/Channel"
      links:
        get:
          operationId: getChannel
          parameters:
            chanID: $response.body#/id
        update:
          operationId: updateChannel
          parameters:
            chanID: $response.body#/id
        disable:
          operationId: disableChannel
          parameters:
            chanID: $response.body#/id
        enable:
          operationId: enableChannel
          parameters:
            chanID: $response.body#/id

    ChannelRes:
      description: Data retrieved.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Channel"
      links:
        update:
          operationId: updateChannel
          parameters:
            chanID: $response.body#/id
        disable:
          operationId: disableChannel
          parameters:
            chanID: $response.body#/id
        enable:
          operationId: enableChannel
          parameters:
            chanID: $response.body#/id
        set_parent_group:
          operationId: setChannelParentGroup
          parameters:
            chanID: $response.body#/id
        remove_parent_group:
          operationId: removeChannelParentGroup
          parameters:
            chanID: $response.body#/id

    ChannelPageRes:
      description: Data retrieved.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ChannelsPage"

    HealthRes:
      description: Service Health Check.
      content:
        application/health+json:
          schema:
            $ref: "#/components/schemas/HealthRes"

    ServiceError:
      description: Unexpected server-side error occurred.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"

  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: |
        * Client access: "Authorization: Bearer <user_access_token>"

security:
  - bearerAuth: []
