openapi: '3.0.1'

info:
  title: IoT Time Series Bulk API
  description: |
    This API allows to bulk import IoT time series data based on files uploaded via IoT File Service. Data import for simulation assets (entities) is supported with up to nano second precision and for performance assets (entities) with up to milli second precision. A bulk import is modeled as asynchronous job whose status can be retrieved after creation. Successfully imported time series data can be retrieved using the read operation.
  version: "3.9.0"
  x-visibility: "external"

servers:
  - url: https://api.eu1.siemens.app/iottsbulk-1000001700/v3
    description: Xcelerator compliant URL

tags:
  - name: Bulk Import Operations
    description: Create and retrieve time series bulk import jobs
  - name: Read Operations
    description: Read Stored Time Series Data

paths:

  /importJobs:
    post:
      summary: Create bulk import job for importing time series data
      tags:
        - Bulk Import Operations
      description: |
        Creates an import job resource to asynchronously import IoT time series data from files uploaded through IoT File Service. After successful creation of an import job, the provided file contents are validated and imported in the background. The status of a job can be retrieved using the returned job ID. Note that in case of validation errors occuring during or after job creation, no time series data is imported from any of the provided files.

        ### Restrictions: ###
        Currently only one asset-aspect (entity-property set) combination can be specified as target of the import. Data for performance assets (entities) must be older than 30 minutes in order to be imported, while for simulation assets (entities) no restriction on minimum age exists. In case of simulation assets (entities), all data must be within the same hour.

        The overall size of the files used to import data for one asset-aspect (entity-property set) combination is limited:
        - For simulation assets (entities), a maximum of 350 MB per hour is allowed

        - For performance assets (entities), a maximum of 1 GB is allowed

        Note that hour and day intervals are fixed with respect to UTC time hours and days.

        A maximum of 100 files can be specified per request.
      operationId: createImportJob
      security:
        - timeseries:
            - iot.bi.w
      requestBody:
        description: Input for time series bulk ingestion job creation. Even though an array of multiple asset-aspect (entity-property set) combinations is offered in the schema, only one combination of asset (entity) and aspect (property set) is supported at the moment. All provided time series data must be related to that combination.
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BulkImportInput'
      responses:
        '202':
          description: bulk timeseries job triggered.
          headers:
            Location:
              schema:
                type: string
              description: URL for getting job status.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobStatus'
        '400':
          description: |
            The request will return HTTP 400 (BAD REQUEST) for the subsequent cases:
            - Unable to find the tenant.
            - Unable to find the asset (entity).
            - Unable to find the aspect (property set).
            - Unable to find the asset type (entity type).
            - The total file size for single / multiple jobs submitted for same hour interval, specific simulation asset (entity) and aspect (property set) exceeds 350 MB.
            - The total file size for single / multiple jobs submitted for same day interval, specific performance asset (entity) and aspect (property set) exceeds 1 GB.
            - The input consists of data for multiple combination of asset (entity) and aspect (property set) name.
            - Error in bulk ingestion: for input file {0}, data should be older than {1} minutes in case of performance asset (entity).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BadRequest'
        '403':
          description: |
            The request will return HTTP 403(Forbidden) for the subsequent cases:
            - User is not having required access to perform this operation on given Asset/Entity {assetId}. Consult tenant admin for appropriate access.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenError'
        '429':
          description: |
            Too many requests that can be arised based on subsequent case:
            - User has sent too many requests in a given amount of time.

            A Retry-After header might be included to this response indicating how long in 'seconds' to wait before making a new request.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Time in seconds to wait before making a new request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TooManyRequests'
        '503':
          description: |
            The request will return HTTP 503(Service Unavailable). Probable cases are:
            - Server not available
            - Migration is in progress.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          description: Other error with any status code and response body format.

  /importJobs/{id}:
    get:
      summary: Retrieve status of bulk import job
      tags:
        - Bulk Import Operations
      operationId: retrieveImportJob
      security:
        - timeseries:
            - iot.bi.r
      parameters:
        - name: id
          in: path
          description: Bulk import job ID as obtained on job creation
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Ok
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobStatus'
        '404':
          description: |
            The request will return HTTP 404 (NOT FOUND) in cases you can find below.
            - The input job id does not exists.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          description: Other error with any status code and response body format.

  /timeseries/{entity}/{propertySetName}:
    get:
      summary: Retrieve time series data
      tags:
        - Read Operations
      description: |
        Retrieve time series data for a single asset (entity) and aspect (property set). Returns data for the specified time range. Returns only the selected properties if 'select' parameter is used.

        The maximum number of time series data items returned per request is defined by parameter 'limit'. In case more time series data items are present in the requested time range, only a subset of data items will be returned. In that case response property 'nextRecord' contains the request URL to fetch the next set of time series data items, by increasing the 'from' parameter accordingly.
      operationId: retrieveTimeseries
      security:
        - timeseries:
            - iot.bts.r
      parameters:
        - name: entity
          in: path
          description: Unique identifier of the asset (entity).
          required: true
          schema:
            type: string
            minLength: 32
            maxLength: 32
            pattern: "[a-f0-9]{32}"
        - name: propertySetName
          in: path
          description: Unique name of the aspect (property set).
          required: true
          schema:
            type: string
        - name: from
          in: query
          description: |
            Beginning of the time range to read (exclusive). 'from' time must be less than 'to' time. Range between 'from' and 'to' time must be less than 90 days.
          required: true
          schema:
            type: string
            format: date-time
        - name: to
          in: query
          description: End of the time range to retrieve (inclusive).
          required: true
          schema:
            type: string
            format: date-time
        - name: limit
          in: query
          description: |
            Maximum number of time series data items to be retrieved. No default value. Based on timeseries data density, as many record values as possible are retrieved for the requested time range under a stipulated overall request time limit of 1 minute.
          required: false
          schema:
            type: integer
        - name: select
          in: query
          description: Comma-separated list of properties to be returned. By default all properties of an(a) aspect (property set) are considered.
          required: false
          schema:
            type: string
      responses:
        '200':
          description: response object
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimeSeries'
        '400':
          description: |
            The request will return 400 BAD REQUEST in cases you can find below.
            - The input limit is less than zero.
            - One or more invalid property names specified.
            - Unable to find the asset (entity).
            - Unable to find the aspect (property set).
            - The time range specified is greater than 90 days.
            - The 'from' time is same as or after the 'to' time specified for range query.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BadRequest'
        '403':
          description: |
            The request will return HTTP 403(Forbidden) for the subsequent cases:
            - User is not having required access to perform this operation on given Asset/Entity {assetId}. Consult tenant admin for appropriate access.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenError'
        '503':
          description: |
            The request will return HTTP 503(Service Unavailable). Probable cases are:
            - Server not available
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          description: Other error with any status code and response body format.

components:

  schemas:

    BulkImportInput:
      type: object
      description: Input for time series bulk import job creation.
      properties:
        data:
          type: array
          maxItems: 1
          items:
            $ref: '#/components/schemas/Data'

    Data:
      type: object
      description: Time series data import schema for one asset-aspect (entity-property set) combination.
      required:
        - entity
        - propertySetName
        - timeseriesFiles
      properties:
        entity:
          type: string
          description: Unique ID of an asset (entity).
          minLength: 32
          maxLength: 32
          pattern: "[a-f0-9]{32}"
        propertySetName:
          type: string
          description: Unique name of a aspect (property set).
        timeseriesFiles:
          type: array
          description: Information on files uploaded with IoT File Service, to be considered as sources for importing time series data. The time ranges defined by properties "from" and "to" for the provided files must not overlap.
          maxItems: 100
          items:
            $ref: '#/components/schemas/FileInfo'

    FileInfo:
      type: object
      description: |
        Descriptor for a file containing IoT time series data. Valid files must have extension '.json' and must contain plaintext JSON with suitable time series data. Suitable time series data follows the format as defined for the 'records' property in the TimeSeries model of this API, i.e., an array with a list of time series records. Time series data must be sorted in chronological order.
      required:
        - filePath
        - from
        - to
      properties:
        filePath:
          type: string
          description: Path of JSON text file containing time series data, uploaded with IoT File Service.
          example: "Folder1/Folder2/Sample.json"
        from:
          type: string
          description: |
            Must match the time of the first time series record appearing in the file, in ISO date-time format. Is used for pre-validations on import job creation.
          format: date-time
        to:
          type: string
          description: |
            Must match the time of the last time series record in the file, in ISO date-time format. Is used for pre-validations on import job creation.
          format: date-time

    BadRequest:
      type: object
      properties:
        timestamp:
          type: string
          format: date-time
        status:
          type: integer
        error:
          type: string
        exception:
          type: string
        message:
          type: string
        path:
          type: string
      example:
        timestamp: "2019-04-08T06:32:01.261+0000"
        status: 400
        error: "Bad Request"
        exception: "com.siemens.mindsphere.iot.exception.IotException"
        message: "[6410] Unable to find the property set - abc"
        path: "/api/iottsbulk/v3/importJobs"

    Error:
      type: object
      properties:
        timestamp:
          type: string
          format: date-time
        status:
          type: integer
        error:
          type: string
        exception:
          type: string
        message:
          type: string
        path:
          type: string
      example:
        timestamp: "2019-04-08T06:32:01.261+0000"
        status: 503
        error: "Service Unavailable"
        message: "Service not available."
        path: "/api/iottsbulk/v3/importJobs"

    ForbiddenError:
      type: object
      properties:
        timestamp:
          type: string
          format: date-time
        status:
          type: integer
        error:
          type: string
        message:
          type: string
        path:
          type: string
      example:
        timestamp: "2019-04-08T06:32:01.261+0000"
        status: 403
        error: "Forbidden"
        message: "[6552] User is not having required access to perform this operation on given Asset/Entity {assetId}. Consult tenant admin for appropriate access."
        path: "/api/iottsbulk/v3/importJobs"

    TooManyRequests:
      type: object
      properties:
        message:
          type: string
      example:
        message: "API rate limit exceeded"

    JobStatus:
      type: object
      properties:
        jobId:
          type: string
          description: Unique import job ID.
        status:
          type: string
          enum:
            - SUBMITTED
            - IN_PROGRESS
            - ERROR
            - SUCCESS
          description: |
            Status of bulk ingest job.
            - SUBMITTED: job has been created but import to time series store not yet started.
            - IN_PROGRESS: import of data has been started. Subsets of the data might already be available to be retrieved.
            - FAILED: time series import has failed. No data is available to be retrieved.
            - SUCCESS: time series data import has been successfully finished.
        message:
          type: string
          description: |
            Contains an error message in case the job status equals FAILED. Possible error messages:
             - The input files are having data for more than one hour.
             - The input files are having duplicate data based on time interval.
             - The input files are having overlapping data based on time interval.
             - Input data is having invalid file format.
             - The input contains data for multiple combinations of asset (entity) and aspect (property set) names.
             - The input 'from' time and actual 'from' time of file does not match.
             - The input 'to' time and actual 'to' time of file does not match.
             - The input files contain variables that are not part of the aspect (property set).
             - The timestamp in input file is not formatted as per ISO time format.
             - The input property value in input file is not of valid type as per property type.
             - Error in bulk ingestion: for input file {0}, input time {1} is not acceptable.
             - Error in bulk ingestion: for input file {0}, invalid time {1} is given as input, up to milli-second time precision is allowed for performance asset (entity).
             - Error in bulk ingestion: given input File {0}, is an invalid JSON file.
             - Aspect {0} is renamed or deleted, please resubmit the job with new aspect.
        jobStartTime:
          type: string
          format: date-time
          description: Start time of data import in ISO date format.
        jobLastModified:
          type: string
          format: date-time
          description: Job last modified time in ISO date format. The backend updates this time whenever the message changes.

    TimeSeries:
      type: object
      required:
        - records
        - nextRecord
      properties:
        records:
          type: array
          description: Array of Timeseries records
          items:
            required:
              - _time
            properties:
              _time:
                type: string
                description: Timestamp of the data points in ISO date format.
                format: date-time
            additionalProperties:
              type: object
              description: Properties and quality code for each property for specific time interval.
            example:
              _time: 2018-09-21T15:00:01.787Z
              exampleproperty0: pressure
              exampleproperty0_qc: pressure_qc
        nextRecord:
          type: string
          description: nextRecord url returned when response is not complete in current response

  securitySchemes:
    timeseries:
      type: oauth2
      description: |
        For **Xcelerator** compliance, please use **authorizationUrl** = https://1000001700.eu1.sws.siemens.com/oauth/authorize and **tokenUrl** = https://1000001700.eu1.sws.siemens.com/oauth/token

      flows:
        authorizationCode:
          authorizationUrl: 'https://1000001700.eu1.sws.siemens.com/oauth/authorize'
          tokenUrl: 'https://1000001700.eu1.sws.siemens.com/oauth/token'
          scopes:
            iot.bi.w: capability to import time series data
            iot.bi.r: read status of bulk time series job or data
            iot.bts.r: read bulk timeseries data
