API capabilities

This tutorial demonstrates how to use the capabilities endpoint to dynamically check which endpoints are supported by the current state of your Nureva® device.

What can be done with the API?

The capabilities endpoint returns an OpenAPI description of Nureva's local APIs. The description returned is a JSON object that follows the OpenAPI Specification, allowing both humans and computers to understand the capabilities of the API. Since the description is machine-readable, developers are able to programmatically extract information from the schema. Developers can also use tools that utilize the description to assist development against the API, such as generating client code, creating mock servers or visualizing the API. We have also extended the OpenAPI Specification with an extension property to indicate which endpoints or attributes are supported by the current state of the Nureva device.

After completing this tutorial, you will know how to make a request to the capabilities endpoint to retrieve the OpenAPI description. Included are instructions for creating an example program to parse through the Nureva extension properties to better understand the capabilities of the Nureva device. For more general information about the OpenAPI description, refer to the OpenAPI Specification.

📘

The OpenAPI description may include endpoints and properties that have not been documented in our tutorials and references due to their unfinished state. These endpoints and properties are subject to change and should not be developed against.


Minimum role required: none

This endpoint does not require authorization, so no role is required.

Overview

  1. Use capabilities to make a request to get the OpenAPI description that describes the capabilities of the audio device.
  2. Understand the Nureva extension object schema.
  3. Write a program to parse through the OpenAPI description to understand the device capabilities.

Instructions

Step 1 - Make a request to get the OpenAPI description

Use the capabilities endpoint:

  1. Set the path with the IP address of the Nureva device followed by /api/v1.
  2. Send the GET request. The code sample below is a request to retrieve the settings for the device with the IP address of 10.0.0.1.
  3. Add the header Nureva-Client-Id as key and integration_app_name as the value.
  4. Add the header Nureva-Client-Version as key and 0.0.1 as the value.
curl --request GET \
     --url https://10.0.0.1/api/v1 \
     --header 'Nureva-Client-Id: integration_app_name' \
     --header 'Nureva-Client-Version: 0.0.1'
  1. If the call is successful, an HTTP status code of 200 OK will be returned. The body of the response will contain the OpenAPI description in JSON format. The schema of the OpenAPI description is described in the OpenAPI Specification schema section.

Step 2 - Understand the Nureva extension object schema

The OpenAPI description has a paths property, which is an object that contains the relative paths for each endpoint. Each endpoint path is a path item object which describes the endpoint and its operations. The operation objects have been extended to include an optional x-nureva property that contains a Nureva extension object. The Nureva extension object contains additional information about the operation such as device requirements. Refer to the capabilities reference to see the schema and a few examples.

Step 3 - Write a program to parse through the OpenAPI description to determine endpoint capabilities

This tutorial will demonstrate how to parse through the OpenAPI description and print out your device's capabilities in Node.js.

First, save the OpenAPI description retrieved in Step 1 to a file. In this example, assume the file is saved as api.json and the paths property in the OpenAPI description contains these endpoints:

{
  "/api/example/endpoint1": {
    "get": {
      "x-nureva": {
        "minimumRoleRequired": "general",
        "requirements": {
          "minimumBars": 1
        }
      }
    }
  },
  "/api/example/endpoint2": {
    "get": {
      "x-nureva": {
        "minimumRoleRequired": "general",
        "requirements": {
          "minimumBars": 2
        },
        "requirementsFailed": {
          "minimumBars": 1
        }
      }
    },
    "patch": {
      "x-nureva": {
        "minimumRoleRequired": "general",
        "requirements": {
          "minimumBars": 1
        },
        "capabilities": {
          "exampleCapability1": {},
          "exampleCapability2": {
            "requirements": {
              "minimumFirmware": "1.5"
            }
          },
          "exampleCapability3": {
            "requirements": {
              "minimumFirmware": "1.9"
            },
            "requirementsFailed": {
              "minimumFirmware": "1.8"
            }
          }
        }
      }
    }
  }
}

You may choose to replace the paths property in your OpenAPI description with the object above to follow the example more closely.

From a new Node.js project, let's start off by reading the OpenAPI description from a file and parsing it into an object. Copy the contents below to the entry point file.

const fs = require('fs');

fs.readFile('./api.json', (error, data) => {
  const openApiDescription = JSON.parse(data);
  getCapabilities(openApiDescription);
});

function getCapabilities(openApiDescription) {
  // To be implemented
}

Let's implement the getCapabilities method to parse the OpenAPI description to check which endpoints are supported by the device.

function getCapabilities(openApiDescription) {
  const paths = openApiDescription.paths;
  // Keep track of device support here
  let support = {};

  if (paths) {
    // Iterate through each endpoint (path item object) in paths
    for (const [pathItemObjectKey, pathItemObject] of Object.entries(paths)) {
      // Iterate through each property in path item object
      for (const [
        pathItemObjectPropertyKey,
        pathItemObjectPropertyObject,
      ] of Object.entries(pathItemObject)) {
        const HttpMethods = [
          'get',
          'put',
          'post',
          'delete',
          'options',
          'head',
          'patch',
          'trace',
        ];
        // Look for the Nureva Extension Object only in the operation objects (HTTP methods)
        if (!HttpMethods.includes(pathItemObjectPropertyKey)) continue;
        const operationKey = pathItemObjectPropertyKey;
        const operationObject = pathItemObjectPropertyObject;
        // Get the Nureva Extension Object from the path item object
        const nurevaExtensionObject = operationObject['x-nureva'];
        const supportOperationPropertyName = `${operationKey} ${pathItemObjectKey}`;
        // If the Nureva Extension Object is null, there are no requirements so the endpoint is supported
        if (!nurevaExtensionObject) {
          support[supportOperationPropertyName] = {
            isEndpointSupported: 'supported',
          };
          continue;
        }

        // Get the requirementsFailed object
        const requirementsFailed = nurevaExtensionObject.requirementsFailed;
        // The endpoint is not supported if this object is non-null
        if (requirementsFailed) {
          support[supportOperationPropertyName] = {
            isEndpointSupported: 'not supported',
          };
        }
        // Otherwise, the endpoint is supported if the object is null
        else {
          support[supportOperationPropertyName] = {
            isEndpointSupported: 'supported',
          };
        }
      }
    }
  }

  console.log(support);
}

Let's run the code and look at the output. Both GET /api/example/endpoint1 and PATCH /api/example/endpoint2 are supported since they do not have any failed requirements. GET /api/example/endpoint2 is not supported because it requires a minimum of two bars.

{
  "get /api/example/endpoint1": {
    "isEndpointSupported": "supported"
  },
  "get /api/example/endpoint2": {
    "isEndpointSupported": "not supported"
  },
  "patch /api/example/endpoint2": {
    "isEndpointSupported": "supported"
  }
}

We can add on to the implementation to also check for capabilities within an endpoint. Let's update the getCapabilities method to parse the capabilities object.

function getCapabilities(openApiDescription) {
  const paths = openApiDescription.paths;
  // Keep track of device support here
  let support = {};

  if (paths) {
    // Iterate through each endpoint (path item object) in paths
    for (const [pathItemObjectKey, pathItemObject] of Object.entries(paths)) {
      // Iterate through each property in path item object
      for (const [
        pathItemObjectPropertyKey,
        pathItemObjectPropertyObject,
      ] of Object.entries(pathItemObject)) {
        const HttpMethods = [
          'get',
          'put',
          'post',
          'delete',
          'options',
          'head',
          'patch',
          'trace',
        ];
        // Look for the Nureva Extension Object only in the operation objects (HTTP methods)
        if (!HttpMethods.includes(pathItemObjectPropertyKey)) continue;
        const operationKey = pathItemObjectPropertyKey;
        const operationObject = pathItemObjectPropertyObject;
        // Get the Nureva Extension Object from the path item object
        const nurevaExtensionObject = operationObject['x-nureva'];
        const supportOperationPropertyName = `${operationKey} ${pathItemObjectKey}`;
        // If the Nureva Extension Object is null, there are no requirements so the endpoint is supported
        if (!nurevaExtensionObject) {
          support[supportOperationPropertyName] = {
            isEndpointSupported: 'supported',
          };
          continue;
        }

        // Get the requirementsFailed object
        const requirementsFailed = nurevaExtensionObject.requirementsFailed;
        // The endpoint is not supported if this object is non-null
        if (requirementsFailed) {
          support[supportOperationPropertyName] = {
            isEndpointSupported: 'not supported',
          };
        }
        // Otherwise, the endpoint is supported if the object is null
        else {
          support[supportOperationPropertyName] = {
            isEndpointSupported: 'supported',
          };
        }

        // Get the capabilities within the endpoint
        const capabilities = nurevaExtensionObject.capabilities;
        if (capabilities) {
          support[supportOperationPropertyName] = {
            ...support[supportOperationPropertyName],
            capabilities: {},
          };
          // Iterate through each capability
          for (const [capabilityKey, capability] of Object.entries(
            capabilities,
          )) {
            const requirementsFailed = capability.requirementsFailed;
            // The capability is not supported if this object is non-null
            if (requirementsFailed) {
              support[supportOperationPropertyName].capabilities = {
                ...support[supportOperationPropertyName].capabilities,
                [capabilityKey]: 'not supported',
              };
            }
            // Otherwise, the capability is supported if the object is null
            else {
              support[supportOperationPropertyName].capabilities = {
                ...support[supportOperationPropertyName].capabilities,
                [capabilityKey]: 'supported',
              };
            }
          }
        }
      }
    }
  }

  console.log(support);
}

The output will now look like this:

{
  "get /api/example/endpoint1": {
    "isEndpointSupported": "supported"
  },
  "get /api/example/endpoint2": {
    "isEndpointSupported": "not supported"
  },
  "patch /api/example/endpoint2": {
    "isEndpointSupported": "supported",
    "capabilities": {
      "exampleCapability1": "supported",
      "exampleCapability2": "supported",
      "exampleCapability3": "not supported"
    }
  }
}

Tutorial complete!

You now know how to check device capabilities.