{"info":{"_postman_id":"e1310099-92e2-46e5-b6d1-b2472e0bac51","name":"FIWARE Accessing Context","description":"<html><head></head><body><p>This tutorial teaches FIWARE users how to alter the context programmatically</p>\n<p>The tutorial builds on the  entities created in the previous \n<a href=\"https://github.com/Fiware/tutorials.Context-Providers/\">stock management example</a>\nand enables a user understand how to write code in an  <a href=\"https://swagger.lab.fiware.org/?url=https://raw.githubusercontent.com/Fiware/specifications/master/OpenAPI/ngsiv2/ngsiv2-openapi.json\">NGSI</a>\ncapable <a href=\"https://nodejs.org/\">Node.js</a> <a href=\"https://expressjs.com/\">Express</a> application in order to retrieve and alter context \ndata. This removes the need to use the command line to invoke cUrl commands.</p>\n<p>The <code>docker-compose</code> file for this tutorial can be found on GitHub: </p>\n<p><img src=\"https://fiware.github.io/tutorials.Accessing-Context/icon/GitHub-Mark-32px.png\" alt=\"GitHub\"> <a href=\"https://github.com/Fiware/tutorials.Accessing-Context\">FIWARE 105: Accessing Context Programmatically</a></p>\n<h1 id=\"accessing-the-context-data\">Accessing the Context Data</h1>\n<p>For a typical smart solution you will be retrieving context data from diverse sources (such as a CRM system, social\nnetworks, mobile apps or IoT sensors for example) and then analysing the context programmatically to make appropriate\nbusiness logic decisions. For example in the stock management demo, the application will need to ensure that the prices\npaid for each item always reflect the current price held within the <strong>Product</strong> entity. For a dynamic system, the \napplication will also need to be able to amend the current context. (e.g. creating or updating data or accuating a sensor\nfor example)</p>\n<p>In general terms, three basic scenarios are defined below:</p>\n<ul>\n<li>Reading Data -  e.g. Give me all the data for the <strong>Store</strong> entity <code>urn:ngsi-ld:Store:001</code></li>\n<li>Aggregation - e.g. Combine the <strong>InventoryItems</strong>  entities for Store <code>urn:ngsi-ld:Store:001</code> with \nthe names and prices of the <strong>Product</strong> entities for sale</li>\n<li>Altering the Context - e.g. Make a sale of a product:<ul>\n<li>Update the daily sales records by the price of the <strong>Product</strong></li>\n<li>decrement the <code>shelfCount</code> of the <strong>InventoryItem</strong> entity</li>\n<li>Create a new Transaction Log record showing the sale has occurred</li>\n<li>Raise an alert in the warehouse if less than 10 objects remain on sale</li>\n<li>etc.</li>\n</ul>\n</li>\n</ul>\n<p>As you can see the business logic behind each request to access/amend context can range from the simple to complex\ndepending upon business needs.</p>\n<h2 id=\"making-http-requests-in-the-language-of-your-choice\">Making HTTP Requests in the language of your choice</h2>\n<p>The <a href=\"http://fiware.github.io/specifications/ngsiv2/latest/\">NGSI</a> specification defines a language agnostic REST API based on the standard usage of HTTP verbs. Therefore context data can be accessed by any programming language, simply through making HTTP requests. </p>\n<p>Here for example is the same HTTP request written in <a href=\"https://secure.php.net/\">PHP</a>, <a href=\"https://Node.js.org/\">Node.js</a> and <a href=\"https://www.oracle.com/java/\">Java</a></p>\n<h4 id=\"php-with-httprequest\">PHP (with <code>HTTPRequest</code>)</h4>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-php\">&lt;?php\n\n$request = new HttpRequest();\n$request-&gt;setUrl('http://localhost:1026/v2/entities/urn:ngsi-ld:Store:001');\n$request-&gt;setMethod(HTTP_METH_GET);\n\n$request-&gt;setQueryData(array(\n  'options' =&gt; 'keyValues'\n));\n\ntry {\n  $response = $request-&gt;send();\n\n  echo $response-&gt;getBody();\n} catch (HttpException $ex) {\n  echo $ex;\n}\n</code></pre>\n<h4 id=\"nodejs-with-request-library\">Node.js (with <code>request</code> library)</h4>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-javascript\">const request = require(\"request\");\n\nconst options = { method: 'GET',\n  url: 'http://localhost:1026/v2/entities/urn:ngsi-ld:Store:001',\n  qs: { options: 'keyValues' }};\n\nrequest(options, function (error, response, body) {\n  if (error) throw new Error(error);\n  console.log(body);\n});\n</code></pre>\n<h4 id=\"java-with-closeablehttpclient-library\">Java (with <code>CloseableHttpClient</code> library)</h4>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-java\">CloseableHttpClient httpclient = HttpClients.createDefault();\ntry {\n    HttpGet httpget = new HttpGet(\"http://localhost:1026/v2/entities/urn:ngsi-ld:Store:001?options=keyValues\");\n\n    ResponseHandler&lt;String&gt; responseHandler = new ResponseHandler&lt;String&gt;() {\n        @Override\n        public String handleResponse(\n                final HttpResponse response) throws ClientProtocolException, IOException {\n            int status = response.getStatusLine().getStatusCode();\n            if (status &gt;= 200 &amp;&amp; status &lt; 300) {\n                HttpEntity entity = response.getEntity();\n                return entity != null ? EntityUtils.toString(entity) : null;\n            } else {\n                throw new ClientProtocolException(\"Unexpected response status: \" + status);\n            }\n        }\n\n    };\n    String body = httpclient.execute(httpget, responseHandler);\n    System.out.println(body);\n} finally {\n    httpclient.close();\n}\n</code></pre>\n<h2 id=\"generating-ngsi-api-clients\">Generating NGSI API Clients</h2>\n<p>As you can see from the examples above, each one uses their own programming paradigm to do the following:</p>\n<ul>\n<li>Create a well formed URL.</li>\n<li>Make an HTTP GET request.</li>\n<li>Retrieve the response.</li>\n<li>Check for an error status and throw an exception if necessary.</li>\n<li>Return the body of the request for further processing.</li>\n</ul>\n<p>Since such boilerplate code is frequently re-used it is usually hidden within a library. </p>\n<p>The <a href=\"https://github.com/swagger-api/swagger-codegen\"><code>swagger-codegen</code></a> tool is able to generate boilerplate API client libraries in a wide variety of programming languages directly from the <a href=\"https://swagger.lab.fiware.org/?url=https://raw.githubusercontent.com/Fiware/specifications/master/OpenAPI/ngsiv2/ngsiv2-openapi.json\">NGSI v2 Swagger Specification</a></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-console\">swagger-codegen generate \\\n  -l javascript \\\n  -i http://fiware.github.io/specifications/OpenAPI/ngsiv2/ngsiv2-openapi.json \n</code></pre>\n<p>The generated client can then be used by the code within your own application.</p>\n<h2 id=\"the-teaching-goal-of-this-tutorial\">The teaching goal of this tutorial</h2>\n<p>The aim of this tutorial is to improve developer understanding of programmatic access of context data\nthrough defining and discussing a series of generic code examples covering common data access scenarios. \nFor this purpose a simple Node.js Express application will be created.</p>\n<p>The intention here is not to teach users how to write an application in Express - indeed any language could\nhave been chosen. It is merely to show how <strong>any</strong> sample programming language could be used alter the\ncontext to achieve the business logic goals. </p>\n<p>Obviously, your choice of programming language will depend upon your own business needs - when reading the code\nbelow please keep this in mind and substitute Node.js with your own programming language as appropriate.</p>\n<h2 id=\"entities-within-a-stock-management-system\">Entities within a stock management system</h2>\n<p>The relationship between our entities is defined as shown:</p>\n<p><img src=\"https://fiware.github.io/tutorials.Accessing-Context/img/entities.png\" alt=\"\"></p>\n<p>The <strong>Store</strong>, <strong>Product</strong> and <strong>InventoryItem</strong> entities will be used to display data on the front-end of our demo application.</p>\n<h1 id=\"architecture\">Architecture</h1>\n<p>This application will make use of only one FIWARE component - the <a href=\"https://catalogue.fiware.org/enablers/publishsubscribe-context-broker-orion-context-broker\">Orion Context Broker</a>. Usage of the Orion Context Broker is sufficient for an application to qualify as <em>“Powered by FIWARE”</em>.</p>\n<p>Currently, the Orion Context Broker relies on open source <a href=\"https://www.mongodb.com/\">MongoDB</a> technology to keep\npersistence of the context data it holds. To request context data from external sources, a simple Context Provider NGSI \nproxy has also been added. To visualise and interact with the Context we will add a simple Express application </p>\n<p>Therefore, the architecture will consist of four elements:</p>\n<ul>\n<li>The Orion Context Broker server which will receive requests using <a href=\"https://swagger.lab.fiware.org/?url=https://raw.githubusercontent.com/Fiware/specifications/master/OpenAPI/ngsiv2/ngsiv2-openapi.json\">NGSI</a></li>\n<li>The underlying MongoDB database associated to the Orion Context Broker server</li>\n<li>The Context Provider NGSI proxy which will will:<ul>\n<li>receive requests using  <a href=\"https://swagger.lab.fiware.org/?url=https://raw.githubusercontent.com/Fiware/specifications/master/OpenAPI/ngsiv2/ngsiv2-openapi.json\">NGSI</a></li>\n<li>makes requests to publicly available data sources using their own APIs in a proprietory format </li>\n<li>returns context data back to the Orion Context Broker in  <a href=\"https://swagger.lab.fiware.org/?url=https://raw.githubusercontent.com/Fiware/specifications/master/OpenAPI/ngsiv2/ngsiv2-openapi.json\">NGSI</a> format.</li>\n</ul>\n</li>\n<li>The Stock Management Frontend which will will:<ul>\n<li>Display store information</li>\n<li>Show which products can be bought at each store</li>\n<li>Allow users to \"buy\" products and reduce the stock count.</li>\n</ul>\n</li>\n</ul>\n<p>Since all interactions between the elements are initiated by HTTP requests, the entities can be containerized and run\nfrom exposed ports. </p>\n<p><img src=\"https://fiware.github.io/tutorials.Accessing-Context/img/architecture.png\" alt=\"\"></p>\n<p>The necessary configuration information can be seen in the services section of the associated <code>docker-compose.yml</code> file. It\nhas been described in a <a href=\"https://github.com/Fiware/tutorials.Context-Providers/\">previous tutorial</a></p>\n<h1 id=\"prerequisites\">Prerequisites</h1>\n<h2 id=\"docker\">Docker</h2>\n<p>To keep things simple both components will be run using <a href=\"https://www.docker.com\">Docker</a>. <strong>Docker</strong> is a container technology\nwhich allows to different components isolated into their respective environments. </p>\n<ul>\n<li>To install Docker on Windows follow the instructions <a href=\"https://docs.docker.com/docker-for-windows/\">here</a></li>\n<li>To install Docker on Mac follow the instructions <a href=\"https://docs.docker.com/docker-for-mac/\">here</a></li>\n<li>To install Docker on Linux follow the instructions <a href=\"https://docs.docker.com/install/\">here</a></li>\n</ul>\n<p><strong>Docker Compose</strong> is a tool for defining and running multi-container Docker applications. A \n<a href=\"https://raw.githubusercontent.com/Fiware/tutorials.Entity-Relationships/master/docker-compose.yml\">YAML file</a> is used\nconfigure the required services for the application. This means all container sevices can be brought up in a single \ncommmand. Docker Compose is installed by default  as part of Docker for Windows and  Docker for Mac, however Linux users \nwill need to follow the instructions found  <a href=\"https://docs.docker.com/compose/install/\">here</a></p>\n<h2 id=\"cygwin\">Cygwin</h2>\n<p>We will start up our services using a simple bash script. Windows users should download <a href=\"www.cygwin.com\">cygwin</a> to provide a\ncommand line functionality similar to a Linux distribution on Windows. </p>\n<h1 id=\"start-up\">Start Up</h1>\n<p>All services can be initialised from the command line by running the bash script provided within the repository:</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-console\">./services create; ./services start;\n</code></pre>\n<p>This command will also import seed data from the previous <a href=\"https://github.com/Fiware/tutorials.Context-Providers\">Stock Management example</a> on startup.</p>\n<blockquote>\n<p>:information_source: <strong>Note:</strong> If you want to clean up and start over again you can do so with the following command:</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-console\">./services stop\n</code></pre>\n</blockquote>\n<h1 id=\"stock-management-frontend\">Stock Management Frontend</h1>\n<p>All the code Node.js Express for the demo can be found within the <code>proxy</code> folder within the GitHub repository.<a href=\"https://github.com/Fiware/tutorials.Accessing-Context/tree/master/proxy\">Stock Management example</a>. The application runs on the following URLs:</p>\n<ul>\n<li><code>http://localhost:3000/app/store/urn:ngsi-ld:Store:001</code></li>\n<li><code>http://localhost:3000/app/store/urn:ngsi-ld:Store:002</code></li>\n<li><code>http://localhost:3000/app/store/urn:ngsi-ld:Store:003</code></li>\n<li><code>http://localhost:3000/app/store/urn:ngsi-ld:Store:004</code></li>\n</ul>\n<blockquote>\n<p>:information_source: <strong>Tip</strong>  Additionally, you can also watch the status of recent requests yourself by\nfollowing the container logs or viewing information on <code>localhost:3000/app/monitor</code> on a web browser.</p>\n<p><img src=\"https://fiware.github.io/tutorials.Accessing-Context/img/monitor.png\" alt=\"FIWARE Monitor\"></p>\n</blockquote>\n<h2 id=\"ngsi-v2-npm-library\">NGSI v2 npm library</h2>\n<p>An NGSI v2 compatible <a href=\"https://github.com/smartsdk/ngsi-sdk-javascript\">npm library</a> has been developed by the\n<a href=\"https://www.smartsdk.eu/\">SmartSDK</a> team. This is a callback-based library which will be used to take care of\nour low level HTTP requests and will simplify the code to be written. The methods exposed in the library map\ndirectly onto the NGSI v2 <a href=\"https://github.com/Fiware/tutorials.CRUD-Operations#what-is-crud\">CRUD operations</a>\nwith the following names:</p>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th>HTTP Verb</th>\n<th><code>/v2/entities</code></th>\n<th><code>/v2/entities/&lt;entity&gt;</code></th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>POST</strong></td>\n<td><a href=\"https://github.com/smartsdk/ngsi-sdk-javascript/blob/master/docs/EntitiesApi.md#createEntity\"><code>createEntity()</code></a></td>\n<td>Not used</td>\n</tr>\n<tr>\n<td><strong>GET</strong></td>\n<td><a href=\"https://github.com/smartsdk/ngsi-sdk-javascript/blob/master/docs/EntitiesApi.md#listEntities\"><code>listEntities()</code></a></td>\n<td><a href=\"https://github.com/smartsdk/ngsi-sdk-javascript/blob/master/docs/EntitiesApi.md#retrieveEntity\"><code>retrieveEntity()</code></a></td>\n</tr>\n<tr>\n<td><strong>PUT</strong></td>\n<td>Not used</td>\n<td>Not used</td>\n</tr>\n<tr>\n<td><strong>PATCH</strong></td>\n<td>Not used</td>\n<td>Not used</td>\n</tr>\n<tr>\n<td><strong>DELETE</strong></td>\n<td>Not used</td>\n<td><a href=\"https://github.com/smartsdk/ngsi-sdk-javascript/blob/master/docs/EntitiesApi.md#removeEntity\"><code>removeEntity()</code></a></td>\n</tr>\n</tbody>\n</table>\n</div><div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th>HTTP Verb</th>\n<th><code>.../attrs</code></th>\n<th><code>.../attrs/&lt;attribute&gt;</code></th>\n<th><code>.../attrs/&lt;attribute&gt;/value</code></th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>POST</strong></td>\n<td><a href=\"https://github.com/smartsdk/ngsi-sdk-javascript/blob/master/docs/EntitiesApi.md#updateOrAppendEntityAttributes\"><code>updateOrAppendEntityAttributes()</code></a></td>\n<td>Not used</td>\n<td>Not used</td>\n</tr>\n<tr>\n<td><strong>GET</strong></td>\n<td><a href=\"https://github.com/smartsdk/ngsi-sdk-javascript/blob/master/docs/EntitiesApi.md#retrieveEntityAttributes\"><code>retrieveEntityAttributes()</code></a></td>\n<td>Not used</td>\n<td><a href=\"https://github.com/smartsdk/ngsi-sdk-javascript/blob/master/docs/AttributeValueApi.md#getAttributeValue\"><code>getAttributeValue()</code></a></td>\n</tr>\n<tr>\n<td><strong>PUT</strong></td>\n<td>Not used</td>\n<td>Not used</td>\n<td><a href=\"https://github.com/smartsdk/ngsi-sdk-javascript/blob/master/docs/AttributeValueApi.md#updateAttributeValue\"><code>updateAttributeValue()</code></a></td>\n</tr>\n<tr>\n<td><strong>PATCH</strong></td>\n<td><a href=\"https://github.com/smartsdk/ngsi-sdk-javascript/blob/master/docs/EntitiesApi.md#updateExistingEntityAttributes\"><code>updateExistingEntityAttributes()</code></a></td>\n<td>Not used</td>\n<td>Not used</td>\n</tr>\n<tr>\n<td><strong>DELETE</strong>.</td>\n<td>Not used</td>\n<td><code>removeASingleAttribute()</code></td>\n<td>Not used</td>\n</tr>\n</tbody>\n</table>\n</div><h2 id=\"analysing-the-code\">Analysing the Code</h2>\n<p>The code under discussion can be found within the <code>store</code> controller in the <a href=\"https://github.com/Fiware/tutorials.Context-Providers/blob/master/proxy/controllers/store.js\">Git Repository</a></p>\n<h3 id=\"initializing-the-library\">Initializing the library</h3>\n<p>We don't want to reinvent the wheel and spend time writing a unnecessary boilerplate code for HTTP access. Therefore\nwe will use the exising <code>ngsi_v2</code>  NPM library. This needs to be included in the header of the file as shown. The\n<code>basePath</code> must also be set - this defines the location of the Orion Context Broker. </p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-javascript\">const NgsiV2 = require('ngsi_v2');\nconst defaultClient = NgsiV2.ApiClient.instance;\ndefaultClient.basePath = process.env.CONTEXT_BROKER || 'http://localhost:1026/v2';\n</code></pre>\n<h3 id=\"reading-store-data\">Reading Store Data</h3>\n<p>This example reads the context data of a given <strong>Store</strong> entity to display the results on screen.\nReading entity data can be done using the <code>apiInstance.retrieveEntity()</code> method. Since the library uses callbacks,\nthey have been wrapped by a <code>Promise</code> function as shown below. The libary function <code>apiInstance.retrieveEntity()</code> \nwill fill out the URL for the GET request and make the necessary HTTP call:</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-javascript\">function retrieveEntity(entityId, opts) {\n  return new Promise(function(resolve, reject) {\n    const apiInstance = new NgsiV2.EntitiesApi();\n    apiInstance.retrieveEntity(entityId, opts, (error, data) =&gt; {\n      return error ? reject(error) : resolve(data);\n    });\n  });\n}\n</code></pre>\n<p>This enables us to wrap the requests in <code>Promises</code> as shown:</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-javascript\">function displayStore(req, res) {\n  retrieveEntity(\n    req.params.storeId, { options: 'keyValues', type: 'Store' })\n  .then(store =&gt; {\n    // If a store has been found display it on screen\n    return res.render('store', { title: store.name, store});\n  })\n  .catch(error =&gt; {\n    debug(error);\n    // If no store has been found, display an error screen\n      return res.render('store-error', {title: 'Error', error});\n  });\n}\n</code></pre>\n<p>Indirectly this is making an HTTP GET request to <code>http://localhost:1026/v2/entities/&lt;store-id&gt;?type=Store&amp;options=keyValues</code>.\nNote the re-use of the Store URN in the incoming request.</p>\n<p>The equivalent cUrl command would be as shown:</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-console\">curl -X GET \\\n  'http://localhost:1026/v2/entities/urn:ngsi-ld:Store:001?options=keyValues'\n</code></pre>\n<p>The response will be as shown below:</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{\n    \"id\": \"urn:ngsi-ld:Store:001\",\n    \"type\": \"Store\",\n    \"address\": {\n        \"streetAddress\": \"Bornholmer Straße 65\",\n        \"addressRegion\": \"Berlin\",\n        \"addressLocality\": \"Prenzlauer Berg\",\n        \"postalCode\": \"10439\"\n    },\n    \"location\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n            13.3986,\n            52.5547\n        ]\n    },\n    \"name\": \"Bösebrücke Einkauf\"\n}\n</code></pre>\n<p>The store data from the HTTP response body is then passed to the PUG rendering engine to display on screen as\nshown below:</p>\n<h4 id=\"httplocalhost3000appstoreurnngsi-ldstore005\"><code>http://localhost:3000/app/store/urn:ngsi-ld:Store:005</code></h4>\n<p><img src=\"https://fiware.github.io/tutorials.Accessing-Context/img/store.png\" alt=\"Store 1\"></p>\n<p>For efficiency, it is important to request as few attributes as possible, in order to reduce network traffic.\nThis optimization has not been made in the code yet.</p>\n<p>An error handler is necessary in case the context data is not available - for example if a user queries for a\nstore that does not exist. This will forward to an error page as shown:</p>\n<h4 id=\"httplocalhost3000appstoreurnngsi-ldstore005-1\"><code>http://localhost:3000/app/store/urn:ngsi-ld:Store:005</code></h4>\n<p><img src=\"https://fiware.github.io/tutorials.Accessing-Context/img/store-error.png\" alt=\"Store 5\"></p>\n<p>The equivalent cUrl command would be as shown:</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-console\">curl -X GET \\\n  'http://localhost:1026/v2/entities/urn:ngsi-ld:Store:001?options=keyValues'\n</code></pre>\n<p>The response has a status of <strong>404 Not Found</strong> with a body as shown below:</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{\n    \"error\": \"NotFound\",\n    \"description\": \"The requested entity has not been found. Check type and id\"\n}\n</code></pre>\n<p>The <code>error</code> object in the <code>catch</code> method hold the error response. This is then displayed on the front end.</p>\n<h3 id=\"aggregating-products-and-inventory-items\">Aggregating Products and Inventory Items</h3>\n<p>This example reads the context data of the current <strong>InventoryItem</strong> entities for a given store and combines\nthe information with the prices from the <strong>Product</strong> entities. The result is information to be displayed on\nthe cash till.</p>\n<p><img src=\"https://fiware.github.io/tutorials.Accessing-Context/img/till.png\" alt=\"Till\"></p>\n<p>Multiple entities can be requested and aggregated by creating a <code>Promise</code> chain or by usign <code>Promise.all</code>. \nHere the <strong>Product</strong>  and <strong>InventoryItems</strong> entities have been requested using the <code>apiInstance.listEntities()</code> \nlibrary method. The presence of the <code>q</code> parameter in the request will filter the list of entities received. </p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-javascript\">function displayTillInfo(req, res) {\n  Promise.all([ \n    listEntities({\n    options: 'keyValues',\n    type: 'Product',\n  }), listEntities({\n    q: 'refStore==' + req.params.storeId,\n    options: 'keyValues',\n    type: 'InventoryItem',\n  })])\n  .then(values =&gt; {\n    // If values have been found display it on screen\n    return res.render('till', { products : values[0], inventory : values[1] });\n  })\n  .catch(error =&gt; {\n    debug(error);\n    // An error occurred, return with no results\n    return res.render('till', { products : {}, inventory : {}});\n  });\n}\n\n\nfunction listEntities(opts) {\n  return new Promise(function(resolve, reject) {\n    const apiInstance = new NgsiV2.EntitiesApi();\n    apiInstance.listEntities(opts, (error, data) =&gt; {\n      return error ? reject(error) : resolve(data);\n    });\n  });\n}\n</code></pre>\n<p>The code used for aggregating the results (displaying the product names for each item stocked) has been delegated\nto a <code>mixin</code> on the front-end. The foreign key aggregation (<code>item.refProduct === product.id</code>) could have been\nadded to the Node.js code if we were passing on aggregated data to another component:</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-pug\">mixin product(item, products)\n  each product in products\n    if (item.refProduct === product.id)\n      span(id=`${product.id}`)\n        strong\n          | #{product.name}\n\n        | &amp;nbsp; @ #{product.price /100} &amp;euro; each\n        | - #{item.shelfCount} in stock\n        |\n\n</code></pre>\n<p>Again an error handler has been created to ensure that if any of the HTTP requests to the Orion Context Broker fail, an empty list of products is returned. </p>\n<p>Retrieving the full list of <strong>Product</strong> entities for each request is not efficient. It would be better to load the list of products from cache, and only update the list if prices have changed. This could be achieved using the NGSI Subscription mechanism which is the subject of a subsequent tutorial.</p>\n<h3 id=\"updating-context\">Updating Context</h3>\n<p>Buying an item will involve decrementing the number of items left on a shelf. The example consists of two linked requests.\nThe reading of the <strong>InventoryItem</strong> entity data can be done using the <code>apiInstance.retrieveEntity()</code> method as shown \npreviously. The data is then ammended  in memory before being sent to the Orion Context Broker using the \n<code>apiInstance.updateExistingEntityAttributes()</code> method.  This is effectively just a wrapper around an HTTP PATCH request to\n<code>http://localhost:1026/v2/entities/&lt;inventory-id&gt;?type=InventoryItem</code>, with a body containing the elements to be updated.\nThere is no error handling on this function, it has been left to a function on the router.</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-javascript\">async function buyItem(req, res) {\n  const inventory = await retrieveEntity(req.params.inventoryId, {\n    options: 'keyValues',\n    type: 'InventoryItem',\n  });\n  const count = inventory.shelfCount - 1;\n  await updateExistingEntityAttributes(\n    req.params.inventoryId,\n    { shelfCount: { type: 'Integer', value: count } },\n    {\n      type: 'InventoryItem',\n    }\n  );\n  res.redirect(`/app/store/${inventory.refStore}/till`);\n}\n\nfunction updateExistingEntityAttributes(entityId, body, opts) {\n  return new Promise(function(resolve, reject) {\n    const apiInstance = new NgsiV2.EntitiesApi();\n    apiInstance.updateExistingEntityAttributes(entityId, body, opts, (error, data) =&gt; {\n      return error ? reject(error) : resolve(data);\n    });\n  });\n}\n</code></pre>\n<p>Care should be taken when amending the context to ensure that changes of state are committed atomically. This is not an issue in Node.JS since it is single threaded - each request but will execute each request one by one. However in multithreaded environments (such as Java for example) it could be possible to service two buy requests concurrently - meaning that the <code>shelfCount</code> will only be reduced once if the requests interleave. This issue can be resolved by the use of a monitor mechanism.</p>\n<p>This is the equivalent of the following cURL commands (plus some business logic)</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code>curl -X GET \\\n  'http://localhost:1026/v2/entities/urn:ngsi-ld:InventoryItem:001/attrs/shelfCount/value'\ncurl -X PATCH \\\n  'http://localhost:1026/v2/entities/urn:ngsi-ld:InventoryItem:006/attrs' \\\n  -H 'Cache-Control: no-cache' \\\n  -H 'Content-Type: application/json' \\\n  -H 'Postman-Token: af6d60fe-ef9f-4d54-8382-3ddd48d43b82' \\\n  -d '{ \"shelfCount\": \n  { \"type\": \"Integer\", \"value\": \"13\" } \n}'\n</code></pre></body></html>","schema":"https://schema.getpostman.com/json/collection/v2.0.0/collection.json","toc":[{"content":"Accessing the Context Data","slug":"accessing-the-context-data"},{"content":"Architecture","slug":"architecture"},{"content":"Prerequisites","slug":"prerequisites"},{"content":"Start Up","slug":"start-up"},{"content":"Stock Management Frontend","slug":"stock-management-frontend"}],"owner":"513743","collectionId":"e1310099-92e2-46e5-b6d1-b2472e0bac51","publishedId":"RW1bnetD","public":true,"customColor":{"top-bar":"FFFFFF","right-sidebar":"4C4C4C","highlight":"233C68"},"publishDate":"2020-01-02T11:02:28.000Z"},"item":[{"name":"Retrieve Data for Store 1","id":"569a70c9-57f4-42e2-bc20-f461b48429f2","request":{"method":"GET","header":[],"body":{"mode":"formdata","formdata":[]},"url":"http://localhost:1026/v2/entities/urn:ngsi-ld:Store:001?options=keyValues","description":"<p>This example returns the context data of <code>urn:ngsi-ld:Store:001</code>.</p>\n<p>It is the equivalent of the following Node.js code:</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-javascript\">function displayStore(req, res) {\n    retrieveEntity(\n        req.params.storeId, { options: 'keyValues', type: 'Store' })\n    .then(store =&gt; {\n        // If a store has been found display it on screen\n        return res.render('store', { title: store.name, store});\n    })\n    .catch(error =&gt; {\n        debug(error);\n        // If no store has been found, display an error screen\n        return res.render('store-error', {title: 'Error', error});\n    });\n}\n</code></pre>\n","urlObject":{"protocol":"http","path":["v2","entities","urn:ngsi-ld:Store:001"],"host":["localhost:1026"],"query":[{"description":{"content":"<ul>\n<li><code>keyValues</code> option in order to get a more compact and brief representation, including just attribute values</li>\n<li><code>values</code> option combined with a list of attribute values  <code>attrs</code>  for an ordered list of attributes only</li>\n</ul>\n","type":"text/plain"},"key":"options","value":"keyValues"},{"disabled":true,"description":{"content":"<p>Entity type, to avoid ambiguity in case there are several entities with the same entity id</p>\n","type":"text/plain"},"key":"type","value":"Store"},{"disabled":true,"description":{"content":"<p>Ordered list of attribute names to display</p>\n","type":"text/plain"},"key":"attrs","value":"name"}],"variable":[]}},"response":[],"_postman_id":"569a70c9-57f4-42e2-bc20-f461b48429f2"},{"name":"Retrieve Data for Store 5","id":"67a92179-027d-473b-89ab-47705def2a81","request":{"method":"GET","header":[],"body":{"mode":"formdata","formdata":[]},"url":"http://localhost:1026/v2/entities/urn:ngsi-ld:Store:005?options=keyValues","description":"<p>This example returns the context data of <code>urn:ngsi-ld:Store:005</code> which does not exist.</p>\n<p>It is the equivalent of the following Node.js code:</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-javascript\">function displayStore(req, res) {\n    retrieveEntity(\n        req.params.storeId, { options: 'keyValues', type: 'Store' })\n    .then(store =&gt; {\n        // If a store has been found display it on screen\n        return res.render('store', { title: store.name, store});\n    })\n    .catch(error =&gt; {\n        debug(error);\n        // If no store has been found, display an error screen\n        return res.render('store-error', {title: 'Error', error});\n    });\n}\n</code></pre>\n<p>Because the response is an error status, the <code>catch</code> clause will be activated and an error message displayed on screen.</p>\n","urlObject":{"protocol":"http","path":["v2","entities","urn:ngsi-ld:Store:005"],"host":["localhost:1026"],"query":[{"description":{"content":"<ul>\n<li><code>keyValues</code> option in order to get a more compact and brief representation, including just attribute values</li>\n<li><code>values</code> option combined with a list of attribute values  <code>attrs</code>  for an ordered list of attributes only</li>\n</ul>\n","type":"text/plain"},"key":"options","value":"keyValues"},{"disabled":true,"description":{"content":"<p>Entity type, to avoid ambiguity in case there are several entities with the same entity id</p>\n","type":"text/plain"},"key":"type","value":"Store"},{"disabled":true,"description":{"content":"<p>Ordered list of attribute names to display</p>\n","type":"text/plain"},"key":"attrs","value":"name"}],"variable":[]}},"response":[],"_postman_id":"67a92179-027d-473b-89ab-47705def2a81"},{"name":"Retrieve All Product Data","id":"6f9391c8-b726-4fad-88c3-f1aceb2338f5","request":{"method":"GET","header":[],"body":{"mode":"formdata","formdata":[]},"url":"http://localhost:1026/v2/entities/?type=Product&options=keyValues","description":"<p>This example returns all the <code>Product</code> entities within the context data.</p>\n<p>It is the equivalent of the following lines of Node.js code:</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code>function displayProducts(req, res) {\n    listEntities({\n        options: 'keyValues',\n        type: 'Product',\n    })\n    .then(products =&gt; {\n        // If products have been found display it on screen\n        return res.render('till', { products });\n    })\n    .catch(error =&gt; {\n        debug(error);\n        // An error occurred, return with no results\n        return res.render('till', { products : {}});\n    });\n}\n</code></pre>","urlObject":{"protocol":"http","path":["v2","entities",""],"host":["localhost:1026"],"query":[{"description":{"content":"<p>Entity type, to avoid ambiguity in case there are several entities with the same entity id</p>\n","type":"text/plain"},"key":"type","value":"Product"},{"description":{"content":"<ul>\n<li><code>keyValues</code> option in order to get a more compact and brief representation, including just attribute values</li>\n<li><code>values</code> option combined with a list of attribute values  <code>attrs</code>  for an ordered list of attributes only</li>\n</ul>\n","type":"text/plain"},"key":"options","value":"keyValues"},{"disabled":true,"description":{"content":"<p>Ordered list of attribute names to display</p>\n","type":"text/plain"},"key":"attrs","value":"name"}],"variable":[]}},"response":[],"_postman_id":"6f9391c8-b726-4fad-88c3-f1aceb2338f5"},{"name":"Retrieve Inventory Items for Store 1","id":"8f03f03b-0e5d-44c1-8490-fbee1d22d972","request":{"method":"GET","header":[],"body":{"mode":"formdata","formdata":[]},"url":"http://localhost:1026/v2/entities/?type=InventoryItem&options=keyValues&q=refStore==urn:ngsi-ld:Store:001","description":"<p>This example returns a filtered list of all <code>InventoryItem</code> entities associated with <code>urn:ngsi-ld:Store:001</code></p>\n<p>It is the equivalent of the following lines of Node.js code:</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code>function displayInventoryItems(req, res) {\n    listEntities({\n        q: 'refStore==' + req.params.storeId,\n        options: 'keyValues',\n        type: 'InventoryItem',\n    })\n    .then(inventory =&gt; {\n        // If inventory items have been found display it on screen\n        return res.render('till', { inventory });\n    })\n    .catch(error =&gt; {\n        debug(error);\n        // An error occurred, return with no results\n        return res.render('till', inventory : {}});\n    });\n</code></pre>","urlObject":{"protocol":"http","path":["v2","entities",""],"host":["localhost:1026"],"query":[{"key":"type","value":"InventoryItem"},{"description":{"content":"<ul>\n<li><code>keyValues</code> option in order to get a more compact and brief representation, including just attribute values</li>\n<li><code>values</code> option combined with a list of attribute values  <code>attrs</code>  for an ordered list of attributes only</li>\n</ul>\n","type":"text/plain"},"key":"options","value":"keyValues"},{"disabled":true,"description":{"content":"<p>Ordered list of attribute names to display</p>\n","type":"text/plain"},"key":"attrs","value":"name"},{"key":"q","value":"refStore==urn:ngsi-ld:Store:001"}],"variable":[]}},"response":[],"_postman_id":"8f03f03b-0e5d-44c1-8490-fbee1d22d972"},{"name":"Retrieve the Shelf Count of an Inventory Item","id":"46c6e18b-ae38-4241-b1d9-afa58ddd6af8","request":{"method":"GET","header":[],"body":{"mode":"formdata","formdata":[]},"url":"http://localhost:1026/v2/entities/urn:ngsi-ld:InventoryItem:001/attrs/shelfCount/value","description":"<p>This example returns the number of items left on the shelf for InventoryItem <code>urn:ngsi-ld:InventoryItem:001</code>.</p>\n<p>It is the equivalent of the following Node.js code:</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code>function getShelfCount(req, res){\n    const inventory = await getAttributeValue(req.params.inventoryId, 'shelfCount', {\n        type: 'InventoryItem',\n    });\n}\n</code></pre>","urlObject":{"protocol":"http","path":["v2","entities","urn:ngsi-ld:InventoryItem:001","attrs","shelfCount","value"],"host":["localhost:1026"],"query":[],"variable":[]}},"response":[],"_postman_id":"46c6e18b-ae38-4241-b1d9-afa58ddd6af8"},{"name":"Update the Shelf Count of an Inventory Item","id":"dea46c63-38bc-4b7c-a693-59cbb598b8cf","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{ \"shelfCount\": \n  { \"type\": \"Integer\", \"value\": \"13\" } \n}"},"url":"http://localhost:1026/v2/entities/urn:ngsi-ld:InventoryItem:006/attrs","description":"<p>This example amends the number of items left on the shelf for <code>InventoryItem</code> <code>urn:ngsi-ld:InventoryItem:001</code> and sets the value to 13.</p>\n<p>It is the equivalent of the following Node.js code:</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code>function updateShelfCount(req, res, count){\n    await updateExistingEntityAttributes(\n        req.params.inventoryId,\n        { \n            shelfCount: { type: 'Integer', value: count } \n            \n        },\n        {\n            type: 'InventoryItem',\n        });\n}\n</code></pre>","urlObject":{"protocol":"http","path":["v2","entities","urn:ngsi-ld:InventoryItem:006","attrs"],"host":["localhost:1026"],"query":[],"variable":[]}},"response":[],"_postman_id":"dea46c63-38bc-4b7c-a693-59cbb598b8cf"}],"event":[{"listen":"prerequest","script":{"id":"a25c858c-0646-4514-85e8-f6f9aeafc5be","type":"text/javascript","exec":[""]}},{"listen":"test","script":{"id":"f81ca278-51ac-41c1-a4e6-66baed8434f2","type":"text/javascript","exec":[""]}}],"variable":[{"key":"orion","value":"localhost:1026","type":"string"}]}