API Guide

This document provides a comprehensive overview of the REST API interactions enabled via the yang-express middleware framework built on Express.js.

While much care has been taken to auto-generate the API endpoint routing to conform as closely as possible to RESTCONF, there are many key deviations and currently no plans to adapt the default REST API generator to be RESTCONF compatible.

Instead, the REST API generated by the YANG data models are much more closely aligned to conventional REST APIs developed for use in the web application development community. Also, there's currently no support for XML but instead follows the YANG-JSON specifications for representing configuration data in JSON.

Below is the primary snippet from server.coffee demonstrating how to use yang-express middleware framework to fire up a web server instance capable of serving up the YANG model-driven CORD reference implementation.

yang = require('yang-js')
app = require('yang-express') ->
  @set 'pkginfo', require('../../package.json')
  @set 'initial state', require('../../sample-data.json')
  @enable 'openapi', 'restjson', 'websocket'
  
  cord = @link yang.require('cord-core')
  xos  = @link yang.require('xos-core')
  
  cord.on 'update', (prop) ->
    console.log "[#{prop.path}] got updated, should consider persisting the change somewhere"

Yep, it's really that simple.

CORD API Reference

The following section presents various CRUD operations that are runtime generated from the cord-core YANG module. These operations are essentially interpreted and served during runtime depending on the YANG runtime model along with the configuration data state at any given point in time. Basically, it performs adaptive API routing and rendering.

Open-API Specification (Swagger 2.0)

Initial partial-support for dynamic openapi specification generation has been introduced with the latest yang-express middleware framework. It can send back JSON or YAML based specification output depending on the requesting client's Accept header. It defaults to JSON output.

You can give it a try by issuing one of the following:

$ curl localhost:5050/swagger.spec
$ curl localhost:5050/swagger.spec -H 'accept: text/yaml'

We'll plan to fully support the openapi specification along the way.

View Subscribers

Valid URIs:

  • GET /cord-core:subscriber
  • GET /cord:subscriber
  • GET /subscriber

Request:

$ curl localhost:5050/cord:subscriber

Response:

{
  "cord-core:subscriber": [
    {
      "id": 1,
      "service-specific-id": 1000,
      "humanReadableName": "cordSubscriber-1",
      "status": "enabled",
      "demo": false,
      "uplink-speed": 1000000000,
      "downlink-speed": 1000000000,
      "kind": "cord-subscriber",
      "related": {}
    },
    {
      "id": 2,
      "service-specific-id": 1001,
      "humanReadableName": "cordSubscriber-2",
      "status": "enabled",
      "demo": false,
      "uplink-speed": 1000000000,
      "downlink-speed": 1000000000,
      "kind": "cord-subscriber",
      "related": {}
    }
  ]
}

View Details for a Subscriber

Valid URIs:

  • GET /cord-core:subscriber/:id
  • GET /cord:subscriber/:id
  • GET /subscriber/:id

Request:

$ curl localhost:5050/cord:subscriber/1

Response:

{
  "cord-core:subscriber": {
    "id": 1,
    "service-specific-id": 1000,
    "humanReadableName": "cordSubscriber-1",
    "status": "enabled",
    "demo": false,
    "uplink-speed": 1000000000,
    "downlink-speed": 1000000000,
    "kind": "cord-subscriber",
    "related": {}
  }
}

Update a Subscriber

Valid URIs:

  • PUT /cord-core:subscriber/:id
  • PUT /cord:subscriber/:id
  • PUT /subscriber/:id

Request:

$ curl -X PUT localhost:5050/cord:subscriber/1 -H 'content-type: application/json' -d '{ "id": 10, "status": "suspended", "services": { "cdn": { "enabled": true } } }'

Response:

{
  "cord-core:subscriber": {
    "id": 10,
    "service-specific-id": 1000,
    "humanReadableName": "cordSubscriber-10",
    "status": "suspended",
    "demo": false,
    "uplink-speed": 1000000000,
    "downlink-speed": 1000000000,
    "kind": "cord-subscriber",
    "related": {},
    "services": {
      "cdn": {
        "enabled": true
      }
    }
  }
}

This operation highlights an interesting scenario related to invisible YANG schema properties. Any configuration tree segment that does not contain any data behave as shadow properties, which basically means they spring to life once valid data gets placed into it but stays invisible otherwise.

Delete a Subscriber

Valid URIs:

  • DELETE /cord-core:subscriber/:id
  • DELETE /cord:subscriber/:id
  • DELETE /subscriber/:id

Request:

$ curl -X DELETE localhost:5050/cord:subscriber/2

Response:

HTTP 204

Create a new Subscriber

Valid URIs:

  • POST /cord-core:subscriber
  • POST /cord:subscriber
  • POST /subscriber

Request:

$ curl -X POST localhost:5050/cord:subscriber -H 'content-type: application/json' -d '{ "id": 12, "service-specific-id": 2020, "demo": true }'

Response:

{
  "cord-core:subscriber": [
    {
      "id": 12,
      "service-specific-id": 2020,
      "demo": true,
      "humanReadableName": "cordSubscriber-12",
      "status": "enabled",
      "uplink-speed": 1000000000,
      "downlink-speed": 1000000000,
      "kind": "cord-subscriber",
      "related": {}
    }
  ]
}

The POST operation is only available on YANG list schema elements as well as rpc and action elements. This operation also accepts bulk create request data by supplying a JSON array.

Describe the Subscriber Model

This operation provides discovery of a given data model's characteristics as well as other data node elements available inside the data model. It can be used by REST API consumers to auto-discover additional paths and metadata associated with the target entity. It's also useful for dynamically generating API documentation for a given data model.

Valid URIs:

  • OPTIONS /cord-core:subscriber
  • OPTIONS /cord:subscriber
  • OPTIONS /subscriber

Request:

$ curl -X OPTIONS localhost:5050/cord:subscriber

Response:

{
  "name": "cord-core:subscriber",
  "kind": "list",
  "path": "/cord-core:subscriber",
  "exists": true,
  "key": "id",
  "description": "Authorative list of all subscriber instances",
  "data": {
    "humanReadableName": {
      "kind": "leaf",
      "config": false,
      "type": {
        "string": {
          "pattern": "/^cordSubscriber-\\w+$/"
        }
      }
    },
    "status": {
      "kind": "leaf",
      "type": {
        "enumeration": {
          "enum": {
            "enabled": {
              "description": "Enabled",
              "value": "1"
            },
            "suspended": {
              "description": "Suspended",
              "value": "2"
            },
            "delinquent": {
              "description": "Delinquent",
              "value": "3"
            },
            "violation": {
              "description": "Copyright Violation",
              "value": "4"
            }
          }
        }
      },
      "default": "enabled"
    },
    "demo": {
      "kind": "leaf",
      "type": "boolean",
      "default": "false"
    },
    "uplink-speed": {
      "kind": "leaf",
      "type": {
        "dev:bandwidth": {
          "range": "1000000..max"
        }
      },
      "default": "1000000000",
      "units": "bps"
    },
    "downlink-speed": {
      "kind": "leaf",
      "type": {
        "dev:bandwidth": {
          "range": "1000000..max"
        }
      },
      "default": "1000000000",
      "units": "bps"
    },
    "id": {
      "kind": "leaf",
      "type": {
        "unique-identifier": {
          "type": {
            "uint32": {
              "range": "1..max"
            },
            "yang:uuid": {
              "pattern": "/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/"
            }
          }
        }
      },
      "mandatory": true
    },
    "kind": {
      "kind": "leaf",
      "type": {
        "identityref": {
          "base": "kind"
        }
      },
      "default": "generic"
    },
    "name": {
      "kind": "leaf",
      "type": {
        "string": {
          "length": "0..255"
        }
      },
      "description": "Specify name of the TenantRoot"
    },
    "service-specific-attribute": {
      "kind": "leaf",
      "type": "string"
    },
    "service-specific-id": {
      "kind": "leaf",
      "type": {
        "unique-identifier": {
          "type": {
            "uint32": {
              "range": "1..max"
            },
            "yang:uuid": {
              "pattern": "/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/"
            }
          }
        }
      },
      "mandatory": true
    },
    "delete": {
      "kind": "action"
    },
    "services": {
      "kind": "container",
      "description": "Contains various services available to the subscriber"
    },
    "device": {
      "kind": "list",
      "key": "mac"
    },
    "subscribed-tenants": {
      "kind": "list",
      "config": false
    }
  }
}

Additional Endpoints

There are a number of additional operational endpoints dynamically rendered for the YANG data model. Since YANG is a hierarchical data modeling language, you can have deeply nested structures of various containers and lists. The appropriate CRUD operations can take effect as deep into the endpoint entity hierarchy according to the YANG schema.

Here's a sample list of various URIs for the Subscriber model:

MethodsEndpoint URI
GET/POST/cord:subscriber
GET/PUT/DELETE/cord:subscriber/:id
GET/PUT/cord:subscriber/:id/services/cdn
GET/POST/cord:subscriber/:id/device
GET/PUT/DELETE/cord:subscriber/:id/device/:mac
GET/PUT/cord:subscriber/:id/device/:mac/features

Validations

Whenever POST/PUT requests are transacted, the incoming data is YANG schema validated to ensure fitness of the data before it gets applied into the runtime configuration data state.

For example, if you tried to POST /cord:subscriber without the mandatory id and service-specific-id properties, it will generate an error and reject the request.

Also, if you tried to PUT using data values that do not conform to the underlying YANG schema for that data element, it will generate an error and reject the requst.

Invalid Request:

$ curl -X PUT localhost:5050/cord:subscriber/10 -H 'content-type: application/json' -d '{ "status": false }'

Error Response:

{
  "error": {
    "message": "[enumeration] type violation for 'false' on enabled,suspended,delinquent,violation"
  }
}

For additional information on comprehensive validation enforcement support, please refer to yang-js YANG compliance status report.