Skip to content

Persisting Device Settings

Table of content

1 Introduction

2 Register app settings

3 Implement settings endpoints

1 Introduction

Third-party apps can integrate into the ctrlX Setup functionality so that their device-specific app settings are saved or restored when a setup is created or applied. Therefore, an app must provide REST API endpoints to access its settings. Settings of third-party apps that are not accessible through settings endpoints will not be included in a ctrlX setup.

2 Register app settings

An app declares the settings to be included into a setup in the "apps"/"rexroth-setup" section of its package manifest.

Each setting consists of a setting name, a description, a URL used to read and update the settings value, and a value type:

  • name (required): Name of the setting. Must be unique within the app's settings list. Serves as key in the ctrlx-setup.json file.
  • description: Short localized description of the setting.
  • url (required): URL used to read (GET) and update (PUT) the setting. A URL starting with a slash is interpreted to be relative to https://\<host>.
  • type: One of
  • "text" (default): The value is saved as a quoted string.
  • "json": The value is saved in JSON object format (see "Merge operation for JSON-formatted settings").
  • "file": The value is persisted as a file.
  • "encryptedFile": The value is persisted as a file. The Setup app encrypts the file content if the setup is created with a password.

package-manifest.json:

{
  "apps": {
    "rexroth-setup": {
      "settings": [
        {
          "name": "foo",
          "description": "Description of foo",
          "url": "/myapp/settings/foo",
          "type": "text"
        },
        {
          "name": "bar",
          "description": "Description of bar",
          "url": "/myapp/settings/bar",
          "type": "json"
        },
        {
          "name": "baz",
          "description": "Description of baz",
          "url": "/myapp/settings/baz",
          "type": "file"
        },
        {
          "name": "secret",
          "description": "Description of secret",
          "url": "/myapp/settings/secret",
          "type": "encryptedFile"
        }
      ]
    }
  }
}

The Setup app calls the URLs with GET or PUT to read or update the settings when a ctrlX setup is created or applied.

Creating a setup with the above setting declarations might, e.g., result in the following ctrlx-setup.json output. The respective files (baz.bin, secret.bin.aes) would be included in the enclosing ctrlx-setup.zip archive.

ctrlx-setup.json:

{
  "apps": {
    "myApp" : {
      "settings": {
        "foo": "some value",
        "bar": {
          "enabled": true,
          "title": "my title"
        },
        "baz": {
          "$path":"apps/myApp/settings/baz.bin"
        },
        "secret": {
          "$path":"apps/myApp/settings/secret.bin.aes"
        }
      }
    }
  }
}

3 Implement settings endpoints

Third-party apps must consider the following conditions and constraints in their implementations:

  • Third-party app settings are applied after apps have been updated and device admin settings (users, scopes, licenses, ...) have been applied.
  • The order in which requests are sent to read or update the settings is undefined.
  • Persisted settings must be usable with newer versions of the app. A newer app version may introduce new settings and properties which must be tolerated (ignored) by older app versions. Incompatible changes (e.g. changing the meaning of enum values) between app versions must be avoided.
  • Third-party apps using the settings type "json" must implement the special semantics described in the following section.

Merge operation for JSON-formatted settings

Setup supports fine-grained modifications by merging the setup content with the current state of the device. The merge operation will extend JSON objects with new properties, e.g. to add users to a users list. Conversely, a property can be removed by setting its value to null.

Third-party apps can use the merge feature by providing their property settings with type "json" and by implementing the following behavior:

Use case Example Description
A non-existing property in the JSON structure will leave existing properties on the device untouched. "users": {} Does not change users on the device
The value of a property in the JSON structure will update the corresponding property value on the device. "users": {"john": {"passwordHash": "..."}} Updates the passwordHash of user "john"
A new property in the JSON structure will be added on the device. "users": {"jane": {"passwordHash": "..."}} Adds new user "jane"
A property with a null value in the JSON structure will remove the corresponding property on the device. "users": {"john": null} Deletes user "john"

Success behavior

  • GET: Apps must respond with status code 200 if the request has succeeded. The response body must contain the setting value in the following format:
  • "text" => text/plain
  • "json" => application/json
  • "file"/"encryptedFile" => application/octet-stream
  • PUT: Apps must apply the setting value contained in the request body and respond with status code 204 and an empty response body if the request has succeeded.

Failure behavior

  • Apps must respond with an error status code (4xx or 5xx) if a GET or PUT request has failed.
  • The response body must contain a JSON object describing the problem by providing an appropriate status code and title (cf. example and schema below).
  • The Setup app reports the problem response of a failed request as an error in the logbook and via Web UI. Processing is continued with the next setting.

Problem response body (example)

{
  "status": 400, 
  "title": "Cannot parse value."
}

Problem schema

Problem:
  type: object
  x-version: '1.0.0'
  description: |
    This document defines a "problem detail" as a way 
    to carry machine-readable details of errors in a 
    HTTP response to avoid the need to define new error 
    response formats for HTTP APIs.
  required:
    - type
    - title
  properties:
    type:
      type: string
      format: uri
      description: |
        A URI reference [RFC3986] that identifies the problem type. 
        This specification encourages that, when de-referenced, 
        it provide human-readable documentation for the problem type 
        (e.g., using HTML [W3C.REC-html5-20141028]). When this member 
        is not present, its value is assumed to be "about:blank".
      default: 'about:blank'
      example: 'https://example.com/probs/out-of-credit'
    title:
      type: string
      description: |
        A short, human-readable summary of the problem type. 
        It SHOULD NOT change from occurrence to occurrence of the problem, 
        except for purposes of localization (e.g., using proactive content 
        negotiation; see [RFC7231], Section 3.4).
        If the response is a diagnostics message, it contains 
        the text associated with the mainDiagnosisCode.
      example: You do not have enough credit.
    status:
      type: integer
      format: int32
      description: |
        The HTTP status code ([RFC7231], Section 6) generated by the 
        origin server for this occurrence of the problem.
      example: 403
    detail:
      type: string
      description: |
        A human-readable explanation specific to this occurrence of the problem.
        If the response is a diagnostics message, it contains the text associated with
        the detailedDiagnosisCode.
      example: Your current balance is 30, but that costs 50.
    instance:
      type: string
      description: |
        A URI reference that identifies the specific occurrence of the problem.
        It may or may not yield further information if de-referenced.
      example: /account/12345/msgs/abc
    code:
      type: string
      deprecated: true
      description: |
        An error code issued by the system that caused the original problem.
        This code can be used to track down the root cause of the error.
      example: F360001
    mainDiagnosisCode:
      type: string
      description: |
        The main diagnosis code is issued by the system that caused the problem.
        This code can be used to track down the root cause and source of the error.
        It can be used to search in the documentation for a solution.
        It SHOULD NOT change from occurrence to occurrence of the same problem.
      example: 080F0100
    detailedDiagnosisCode:
      type: string
      description: |
        The detailed diagnosis code issued by the system that caused the problem.
        This code can be used to track down the detailed cause and source of the error.
        It can be used to search in the documentation for a solution.
        The detailed diagnosis code gives additional information about the cause of the error.
        It SHOULD NOT change from occurrence to occurrence of the same problem.
      example: '00666001'
    dynamicDescription:
      type: string
      description: |
        A dynamic description gives detailed information about the occurrence of a problem.
        It can change between different occurrences of the same error.
      example: value = -4.5
    severity:
      type: string
      description: '
        Severity of a problem as defined RFC5424 of the Syslog standard, see https://tools.ietf.org/html/rfc5424'
      example: Error
      enum:
        - Emergency
        - Alert
        - Critical
        - Error
        - Warning
        - Notice
        - Informational
        - Debug
    links:
      type: array
      description: |
        Collection of links to fix the problem. E.g. links to online user manual,
        to an online community (using tags) or a developer chat (e.g. Slack).
      items:
        type: string
        format: uri
        example: https://stackoverflow.com/questions/tagged/boschrexroth+controlx+f360001
    moreInfo:
      type: object
      description: |
        The moreInfo field allows you to add additional information that might be necessary to debug or trace down the cause of an error.
        It might also contain information what is needed to resolve the problem.
        There are loosely specified fields for specific problems that can be used in these situations, e.g.:
        - On a 403 Forbidden error: The moreInfo can contain a field "requiredPermissions" with the required permissions seperated by comma
        - On a 402 Payment required: The moreInfo can contain a field "requiredLicenses" with the required licenses seperated by comma
      additionalProperties: true
      example:
        trace: ExampleClass.cpp#452 Null pointer exception
        class: ExampleClass
    cause:
      type: array
      description: |
        Sometimes a problem might be caused by problems that occur in other components or are the root cause of a problem. In this
        case the related problems can be added as a cause. Since it might be possible that multiple components have problems on a single
        action, cause is an array. E.g. Loading failed because several components where not able to load the configuration.
      items:
        $ref: "#/Problem"