openapi: 3.0.1
info:
  title: IoT TS Stream Registration API
  description: >-
    ## This is the Iot Timeseries Stream registration server for the streaming functionality.

    **This API provides basic CRUD functionality over Stream Registrations.**
     - **Stream Registration** can be defined as a logical entity with *filter* (assets, aspects and variables) for which data is to be streamed to the streaming component along with the information about customer's streaming component or the *destination*.
     - **Stream** can be defined as a component that enables flow of the data from one point to another - e.g. Kinesis or Kafka.

  version: 3.0.0

servers:
  - url: https://{subdomain}.{region}.mindsphere.io/api/iottsstreams/v3
tags:
  - name: registrations
    description: Stream registrations
paths:
  /registrations:
    get:
      tags:
        - registrations
      summary: Gets all registrations for the tenant
      operationId: findAllRegistrations
      responses:
        '200':
          description: successful operation
          content:
            application/json:
              schema:
                type: object
                properties:
                  regisrations:
                    type: array
                    items:
                      $ref: '#/components/schemas/registration'
                  page:
                    $ref: "#/components/schemas/Page"
        '400':
          description: Tenant is not provisioned for the functionality
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
              examples:
                Tenant not provisioned:
                  $ref: "#/components/examples/tenantNotProvisioned"

        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Generic"
              examples:
                unathorized:
                  $ref: "#/components/examples/unauthorized"
        '403':
          description: The scope required for accessing this API is missing
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
              examples:
                Forbidden:
                  $ref: "#/components/examples/forbidden"
        '503':
          description: Service unavailable
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
              examples:
                serviceUnavailable:
                  $ref: "#/components/examples/serviceUnavailable"
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Generic"
              examples:
                InternalError:
                  $ref: "#/components/examples/InternalError"
    post:
      tags:
        - registrations
      summary: Create a registration for specific assets and destination.
      operationId: createRegistration
      requestBody:
        $ref: '#/components/requestBodies/registrationCreateBody'
      responses:
        '201':
          description: successful operation
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/registrationRespCreated'
        '400':
          description: |
            Is yielded in following scenarios -

            1. If tenant is not provisioned for the functionality
            2. If the request body JSON is invalid
            3. Destination type is invalid (is not one of KINESIS or KAFKA)
            4. More than 50 Assets have been bound to a registration
            5. More than 1 destinations are bound to one registration.
            6. One or more connection details are missing.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
              examples:
                Tenant not provisioned:
                  $ref: "#/components/examples/tenantNotProvisioned"
                Invalid request body:
                  $ref: "#/components/examples/InvalidRequestBody"
                Invalid Destination type:
                  $ref: "#/components/examples/InvalidDestinationType"
                Number of assets excceds 50 assets per registration limit :
                  $ref: "#/components/examples/TooManyAssets"
                Number of Destinations Exceeds 1 destinstion per registration limit:
                  $ref: "#/components/examples/TooManyDestinations"
                Missing connection details:
                  $ref: "#/components/examples/ConnectionParamMissing"
        '404':
          description: |
            Is yeilded if any of asset, aspect or variables is not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
              examples:
                Asset not found:
                  $ref: "#/components/examples/NotFoundAsset"

        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Generic"
              examples:
                unathorized:
                  $ref: "#/components/examples/unauthorized"
        '403':
          description: The scope required for accessing this API is missing
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
              examples:
                Forbidden:
                  $ref: "#/components/examples/forbidden"
        '503':
          description: Service unavailable
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
              examples:
                serviceUnavailable:
                  $ref: "#/components/examples/serviceUnavailable"
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Generic"
              examples:
                InternalError:
                  $ref: "#/components/examples/InternalError"

  /registrations/{id}/validationJob:
    post:
      tags:
        - registrations
      summary: Validates specific regisration identified by Id. If status field in response is UPDATED/BROKEN/CREATED, user is advised to call this API. This is an async API. User is advised to poll for the status of the validation.
      operationId: validateRegistartionById
      parameters:
        - name: id
          in: path
          description: ID of the registration to be validated.
          required: true
          schema:
            type: string
      responses:
        '202':
          description: Accepted. User should use the get by Id API to poll the registration status.

        '404':
          description: Registration Id not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
              examples:
                Registration Id not found:
                  $ref: "#/components/examples/NotFoundRegistrationId"
        '400':
          description: |
            Is yielded in following scenarios -
            1. Invalid action:The registration is already in VALIDATED / VALIDATING state
            2. Tenant is not provisioned for the iot time-series streaming functionality
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
              examples:
                Invalid operation:
                  $ref: "#/components/examples/InvalidOperation"
                Tenant not provisioned:
                  $ref: "#/components/examples/tenantNotProvisioned"
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Generic"
              examples:
                unathorized:
                  $ref: "#/components/examples/unauthorized"
        '403':
          description: The scope required for accessing this API is missing
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
              examples:
                Forbidden:
                  $ref: "#/components/examples/forbidden"
        '503':
          description: Service unavailable
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
              examples:
                serviceUnavailable:
                  $ref: "#/components/examples/serviceUnavailable"
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Generic"
              examples:
                InternalError:
                  $ref: "#/components/examples/InternalError"

  /registrations/{id}:
    get:
      tags:
        - registrations
      summary: Gets specific registration by id. This API should also be used to poll the status of a registration that has been submitted for validation. Dependening upon the validation, the status field in response may be VALIDATING/VALIDATED/BROKEN
      operationId: findRegistrationById
      parameters:
        - name: id
          in: path
          description: ID of the registration to be retrieved
          required: true
          schema:
            type: string
      responses:
        '200':
          description: |
            Successful operation.

            **The below table defines meaning of status values-**
             | Status            | Description                                                     |
            | ---------------- | ----------------------------------------------------------------|
            | CREATED          | Registration has been created, but user needs to call ```/registrations/{id}/validate``` API to get it working     |
            | VALIDATING       | Registration is in process of validation|
            | VALIDATED        | Registration has been validated and is working|
            | UPDATED          | Registration has recently been patched with a destination update. It needs to be re-validated. user needs to call ```/registrations/{id}/validate``` API to get it working|
            | BROKEN           | Registration is broken. ```error``` should give more insights|
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/registration'
              examples:
                Valid Registration:
                  $ref: "#/components/examples/registrationPatchRespValidated"
                Broken Registration:
                  $ref: "#/components/examples/registrationBroken"
        '400':
          description: Tenant not provisioned for the functionality
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
              examples:
                Tenant not provisioned:
                  $ref: "#/components/examples/tenantNotProvisioned"
        '404':
          description: Invalid id value
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
              examples:
                Registration Id not found:
                  $ref: "#/components/examples/NotFoundRegistrationId"
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Generic"
              examples:
                unathorized:
                  $ref: "#/components/examples/unauthorized"
        '403':
          description: The scope required for accessing this API is missing
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
              examples:
                Forbidden:
                  $ref: "#/components/examples/forbidden"
        '503':
          description: Service unavailable
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
              examples:
                serviceUnavailable:
                  $ref: "#/components/examples/serviceUnavailable"
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Generic"
              examples:
                InternalError:
                  $ref: "#/components/examples/InternalError"
    delete:
      tags:
        - registrations
      summary: Delete a registration by Id
      parameters:
        - name: id
          in: path
          description: ID of the registration to be retrieved
          required: true
          schema:
            type: string
      responses:
        '200':
          description: successful operation
        '400':
          description: Tenant is not provisioned
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
              examples:
                Tenant not provisioned:
                  $ref: "#/components/examples/tenantNotProvisioned"
        '404':
          description: Registration Id not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
              examples:
                Registration Id not found:
                  $ref: "#/components/examples/NotFoundRegistrationId"
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Generic"
              examples:
                unathorized:
                  $ref: "#/components/examples/unauthorized"
        '403':
          description: The scope required for accessing this API is missing
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
              examples:
                Forbidden:
                  $ref: "#/components/examples/forbidden"
        '503':
          description: Service unavailable
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
              examples:
                serviceUnavailable:
                  $ref: "#/components/examples/serviceUnavailable"
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Generic"
              examples:
                InternalError:
                  $ref: "#/components/examples/InternalError"
components:
  requestBodies:
    registrationCreateBody:
      description: |
        Create a new registration.  The status field in response is CREATED for all new registrations. User needs to validate the created stream using the validation API.

        There are two fields here - **filter** and **destination**.

        1. filter - This field contains the assets / aspects for which a new stream registration is to be created. After the rehistration is validated, the timeseries data corresponding to the filter will be streamed to the streaming component defined by the user.
        2. destination - This field contains the streaming component information to be provided by the user. It contains following fields -

              - **host** - The kafka endpoint/bootstrap server address for Kafka and kinesisArn for Kinesis.
              - **user** - Stream name for kafka. For kinesis, the *user* field is not required.
              - **keyId** - Not required if Kafka is on same machine. AWS Access key Id is required for aws managed kafka and kinesis.
              - **keySecret** - Not required if Kafka is on same machine. AWS secret key is required for aws managed kafka and kinesis.
      required: true
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/registrationCreate"
          examples:
            Kinesis:
              $ref: '#/components/examples/registrationCreateKinesis'
            Kafka:
              $ref: '#/components/examples/registrationCreateKafka'
    registrationPatchBody:
      description: |
        patch an existing registration either by addition/deletion of an asset/aspect or the destination.

        *Note- Only one out of filter and destination can be patched at a time.*

        **Patch can be used in following ways** -
        1. To create/delete multiple assets. Multiple actions on multiple assets are supported, but single asset cannot be mapped to more than one action. e.g. CREATE and DELETE both cannot be used with a single asset.
        2. To create/delete multiple aspects. **Aspects**, in the context of current API, are a child element of **assets**. Hence, when performing one or more actions on aspects, the action on the parent level (action mapped to parent asset) must be UPDATE.
        3. There are two fields that can be patched - **filter** and **destination**. Only the field that is to be patched should be in the request body. If the **filter** is getting patched, there's no need for **destination**.
        4. Only one of the fields can be patched at a time.
        5. For destination patching, the action **UPDATE** must be used. Anything else will yield an error.

      required: true
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/registrationPatch"
          examples:
            Delete Asset:
              $ref: "#/components/examples/registrationPatchDeleteAsset"
            Add new Asset:
              $ref: "#/components/examples/registrationPatchAddAsset"
            Add new Aspect:
              $ref: "#/components/examples/registrationPatchAddAspect"
            Delete Aspect:
              $ref: "#/components/examples/registrationPatchDeleteAspect"
            Update Destination:
              $ref: "#/components/examples/registrationPatchUpdateDestination"

  examples:
    registrationRespKinesis:
      value: {"id":"9225c713-e11d-4617-97db-d08d1480ceea","status":"CREATED","filter":[{"assetId":"b727627746644a5296069e92cbd45d8f","aspects":[{"name":"carAspect","variables":["rpm"]}]}],"destination":{"type":"KINESIS","connectionDetails":{"host":"exampleHost"}},"etag":0}
    registrationRespKafka:
      value: {"id":"9225c713-e11d-4617-97db-d08d1480ceea","status":"CREATED","filter":[{"assetId":"b727627746644a5296069e92cbd45d8f","aspects":[{"name":"carAspect","variables":["rpm"]}]}],"destination":{"type":"KAFKA","connectionDetails":{"host":"samplehost.sampledomain.com", "user":"my-kafka"}},"etag":0}
    registrationRespKafkaValidating:
      value: {"id":"9225c713-e11d-4617-97db-d08d1480ceea","status":"VALIDATING","filter":[{"assetId":"b727627746644a5296069e92cbd45d8f","aspects":[{"name":"carAspect","variables":["rpm"]}]}],"destination":{"type":"KAFKA","connectionDetails":{"host":"samplehost.sampledomain.com", "user":"my-kafka"}},"etag":0}
    registrationCreateKinesis:
      value: {"filter":[{"assetId":"b727627746644a5296069e92cbd45d8f","aspects":[{"name":"carAspect","variables":["rpm"]}]}],"destination":{"type":"KINESIS","connectionDetails":{"host":"arn:aws:kinesis:*:111122223333:stream/my-stream","keyId":"String","keySecret":"String"}}}
    registrationCreateKafka:
      value: {"filter":[{"assetId":"b727627746644a5296069e92cbd45d8f","aspects":[{"name":"carAspect","variables":["rpm"]}]}],"destination":{"type":"KAFKA","connectionDetails":{"host":"127.0.0.1","user":"my-stream","keyId":"String","keySecret":"String"}}}
    registrationPatchDeleteAsset:
      value: {"filter": [{"action":"DELETE","assetId":"b727627746644a5296069e92cbd45d8f"},{"action":"DELETE","assetId":"cft5627746644a5dfscvvetbswd45d8f"}]}
    registrationPatchAddAsset:
      value: {"filter": [{"action":"CREATE","assetId":"b727627746644a5296069e92cbd45d8f"},{"action":"CREATE","assetId":"cft5627746644a5dfscvvetbswd45d8f"}]}
    registrationPatchAddAspect:
      value: {"filter": [{"action":"UPDATE","assetId":"b727627746644a5296069e92cbd45d8f","aspects":[{"name":"carAspect","action":"CREATE"}]}]}
    registrationPatchDeleteAspect:
      value: {"filter": [{"action":"UPDATE","assetId":"b727627746644a5296069e92cbd45d8f","aspects":[{"name":"carAspect","action":"DELETE"},{"name":"carAspect2","action":"DELETE"}]}]}
    registrationPatchUpdateDestination:
      value: {"destination": {"type":"KAFKA","action":"UPDATE","connectionDetails":{"host":"127.0.0.1","user":"my-stream","keyId":"String","keySecret":"String"}}}
    registrationPatchRespUpdated:
      value: {"id":"9225c713-e11d-4617-97db-d08d1480ceea","status":"UPDATED","filter":[{"assetId":"b727627746644a5296069e92cbd45d8f","aspects":[{"name":"carAspect","variables":["rpm"]}]}],"destination":{"type":"KINESIS","connectionDetails":{"host":"exampleHost"}},"etag":1}
    registrationPatchRespValidated:
      value: {"id":"9225c713-e11d-4617-97db-d08d1480ceea","status":"VALIDATED","filter":[{"assetId":"b727627746644a5296069e92cbd45d8f","aspects":[{"name":"carAspect","variables":["rpm"]}]}],"destination":{"type":"KINESIS","connectionDetails":{"host":"exampleHost"}},"etag":1}
    registrationBroken:
      value: {"id":"9225c713-e11d-4617-97db-d08d1480ceea","status":"BROKEN","error":{"code":6066,"cause":"Destination host authentication failed"},"filter":[{"assetId":"b727627746644a5296069e92cbd45d8f","aspects":[{"name":"carAspect","variables":["rpm"]}]}],"destination":{"type":"KINESIS","connectionDetails":{"host":"exampleHost"}},"etag":0}
    tenantNotProvisioned:
      value: {"errors":[{"code":"6600","logref":"d665bb74-57f0-11ea-82b4-0242ac130003","message":"[6600] Tenant is not provisioned for the functionality"}]}
    NotFoundAsset:
      value: {"errors":[{"code":"6408","logref":"d665bb74-57f0-11ea-82b4-0242ac130003","message":"[6408] Asset <assetId> not found"}]}
    NotFoundRegistrationId:
      value: {"errors":[{"code":"6604","logref":"d665bb74-57f0-11ea-82b4-0242ac130003","message":"[6604]Registration Id <registrationId> not found"}]}
    InvalidDestinationType:
      value: {"errors":[{"code":"6601","logref":"d665bb74-57f0-11ea-82b4-0242ac130003","message":"[6601] Destination type is not valid"}]}
    InvalidActionType:
      value: {"errors":[{"code":"6603","logref":"d665bb74-57f0-11ea-82b4-0242ac130003","message":"[6603]Action type is invalid - should be one of CREATE, UPDATE or DELETE"}]}
    InvalidParentActionType:
      value: {"errors":[{"code":"6606","logref":"d665bb74-57f0-11ea-82b4-0242ac130003","message":"[6606]Parent Action type is invalid - Must be UPDATE"}]}
    InvalidOperation:
      value: {"errors":[{"code":"6602","logref":"d665bb74-57f0-11ea-82b4-0242ac130003","message":"[6602]Current registration is already in <VALIDATING / VALIDATED> state"}]}
    InvalidRequestBody:
      value: {"errors":[{"code":"6605","logref":"d665bb74-57f0-11ea-82b4-0242ac130003","message":"[6605]Request body is invalid. Exception - {invalid json}"}]}
    TooManyAssets:
      value: {"errors":[{"code":"6606","logref":"d665bb74-57f0-11ea-82b4-0242ac130003","message":"[6606]More than 50 assets specified for the registration"}]}
    TooManyDestinations:
      value: {"errors":[{"code":"6607","logref":"d665bb74-57f0-11ea-82b4-0242ac130003","message":"[6607]Only one destination per registration is allowed."}]}
    ConnectionParamMissing:
      value: {"errors":[{"code":"6608","logref":"d665bb74-57f0-11ea-82b4-0242ac130003","message":"[6608]Parameter {} is missing from connection details"}]}
    forbidden:
      value: {"errors":[{"code":"mdsp.core.iottsstream.forbidden","logref":"d665bb74-57f0-11ea-82b4-0242ac130003","message":"Insufficient scopes to access the API"}]}
    InternalError:
      value: {"errors":[{"code":"mdsp.core.iottsstream.InternalError","logref":"d665bb74-57f0-11ea-82b4-0242ac130003","message":"Something went wrong with your request. Please try again."}]}
    unauthorized:
      value: {"id":"3fa85f64-5717-4562-b3fc-2c963f66afa6","message":"Not authorized to access this resource."}
    serviceUnavailable:
      value: {"id":"3fa85f64-5717-4562-b3fc-2c963f66afa6","message":"Service Unavailable."}

  schemas:
    variables:
      type: array
      items:
        type: string
        example: "rpm"
    aspects:
      type: array
      items:
        type: object
        properties:
          name:
            type: string
            example: "carAspect"
          variables:
            $ref: "#/components/schemas/variables"
      required:
        - name
        - variables
    aspectsPatch:
      type: array
      items:
        type: object
        properties:
          name:
            type: string
            example: "carAspect"
          action:
            $ref: "#/components/schemas/action"
          variables:
            $ref: "#/components/schemas/variables"
      required:
        - name
        - variables
    assets:
      type: array
      items:
        type: object
        properties:
          assetId:
            type: string
            example: "b727627746644a5296069e92cbd45d8f"
          aspects:
            $ref: "#/components/schemas/aspects"
        required:
          - id
          - aspects
    assetsPatch:
      type: array
      items:
        type: object
        properties:
          assetId:
            type: string
            example: "b727627746644a5296069e92cbd45d8f"
          action:
            $ref: "#/components/schemas/action"
          aspects:
            $ref: "#/components/schemas/aspectsPatch"
        required:
          - id
          - aspects
    action:
      type: string
    status:
      type: string
      enum: [CREATED, UPDATED, VALIDATED, VALIDATING, BROKEN]
    error:
      type: object
      properties:
        code:
          type: string
          example: ""
        cause:
          type: string
          example: ""
    connectionDetails:
      type: object
      properties:
        host:
          type: string
        user:
          type: string
        keyId:
          type: string
        keySecret:
          type: string
    connectionDetailsResp:
      type: object
      properties:
        host:
          type: string
        user:
          type: string
    destination:
      type: object
      properties:
        type:
          type: string
        connectionDetails:
          $ref: '#/components/schemas/connectionDetails'
    destinationPatch:
      type: object
      properties:
        type:
          type: string
        action:
          $ref: "#/components/schemas/action"
        connectionDetails:
          $ref: '#/components/schemas/connectionDetails'
    destinationResp:
      type: object
      properties:
        type:
          type: string
          enum: [KINESIS, KAFKA, EVENTHUB]
        connectionDetails:
          $ref: '#/components/schemas/connectionDetailsResp'
    Page:
      type: object
      properties:
        size:
          type: integer
        totalElements:
          type: integer
        totalPages:
          type: integer
        number:
          type: integer
    registration:
      type: object
      properties:
        id:
          type: string
          example: "9225c713-e11d-4617-97db-d08d1480ceea"
        status:
          $ref: "#/components/schemas/status"
        error:
          $ref: "#/components/schemas/error"
        filter:
          $ref: "#/components/schemas/assets"
        destination:
          $ref: "#/components/schemas/destinationResp"
        etag:
          type: integer
          example: 0
      required:
        - assets
        - destination
    registrationCreate:
      type: object
      properties:
        filter:
          $ref: "#/components/schemas/assets"
        destination:
          $ref: "#/components/schemas/destination"
      required:
        - filter
        - destination
    registrationRespCreated:
      type: object
      properties:
        id:
          type: string
          example: "9225c713-e11d-4617-97db-d08d1480ceea"
        status:
          $ref: "#/components/schemas/status"
        filter:
          $ref: "#/components/schemas/assets"
        destination:
          $ref: "#/components/schemas/destinationResp"
        etag:
          type: integer
          example: 0
      required:
        - assets
        - destination
    registrationPatchResp:
      type: object
      properties:
        id:
          type: string
          example: "9225c713-e11d-4617-97db-d08d1480ceea"
        status:
          $ref: "#/components/schemas/status"
        filter:
          $ref: "#/components/schemas/assets"
        destination:
          $ref: "#/components/schemas/destinationResp"
        etag:
          type: integer
          example: 1
      required:
        - assets
        - destination
    registrationPatch:
      type: object
      properties:
        filter:
          $ref: "#/components/schemas/assetsPatch"
        destination:
          $ref: "#/components/schemas/destinationPatch"
      required:
        - assets
        - destination
    ErrorMsg:
      type: object
      properties:
        code:
          type: string
        logref:
          type: string
        message:
          type: string
    Errors:
      type: array
      items:
        $ref: "#/components/schemas/ErrorMsg"
    ApiError:
      type: object
      properties:
        errors:
          $ref: "#/components/schemas/Errors"
    Generic:
      type: object
      properties:
        id:
          type: string
        message:
          type: string
