{"activeVersionTag":"latest","latestAvailableVersionTag":"latest","collection":{"info":{"_postman_id":"b240d096-0af8-44c3-9a5e-22fee62f49e7","name":"FIWARE Subscriptions","description":"The tutorial builds on the entities and [Stock Management Frontend](https://github.com/Fiware/tutorials.Subscriptions/tree/master/proxy)\napplication created in the previous [example](https://github.com/Fiware/tutorials.Accessing-Context/) to enable users to understand\nthe [NGSI](https://swagger.lab.fiware.org/?url=https://raw.githubusercontent.com/Fiware/specifications/master/OpenAPI/ngsiv2/ngsiv2-openapi.json)\nSubscribe/Notify paradigm and how to use NGSI subscriptions within their own code.\n\nThe `docker-compose` file for this tutorial can be found on GitHub: \n\n![GitHub](https://fiware.github.io/tutorials.Subscriptions/icon/GitHub-Mark-32px.png) [FIWARE 106: Subscribing to Changes in Context](https://github.com/Fiware/tutorials.Subscriptions)\n\n# Subscribing to Changes of State\n\n> \"Don't call us, we'll call you\"\n>\n> — Dorothy Kilgallen (The Voice Of Broadway)\n\nWithin the FIWARE platform, an entity represents the state of a physical or conceptural object which exists in the real world. \nEvery smart solution needs to know the current state of these object at any given moment in time. \n\nThe context of each of these entities is constantly changing. For example, within the stock management example, the context will\nchange as new stores open up, products are sold, prices change and so on. For a smart solution based on IoT sensor data, this issue\nis even more pressing as the system will constantly be reacting to changes in the real world.\n\nUntil now all the operations we have used to change the state of the system have been **synchronous** - changes have been made by \ndirectly by a user or application and they have been informed of the result. The Orion Context Broker offers also an **asynchronous**\nnotification mechanism - applications can subscribe to changes of context information so that they can\nbe informed when something happens. This means the application does not need to continously poll or repeat query requests.\n\nUse of the subscription mechanism will therefore reduce both the volume of requests and amount of data being passed between components\nwithin the system. This reduction in network traffic will improve the overall responsiveness.\n\n## Entities within a stock management system\n\nThe relationship between our entities is defined as shown:\n\n![](https://fiware.github.io/tutorials.Subscriptions/img/entities.png)\n\n\n## Stock Management Front-End\n\nIn the previous [tutorial](https://github.com/Fiware/tutorials.Accessing-Context/), a simple Node.js Express application was created.\nThis tutorial will use the monitor page to watch the status of recent requests, and a store page to buy products. Once the services\nare running these pages can be accessed from the following URLs:\n\n#### Event Montior\n\nThe event monitor can be found at: `http://localhost:3000/app/monitor`\n\n![FIWARE Monitor](https://fiware.github.io/tutorials.Subscriptions/img/monitor.png)\n\n#### Store 002\n\nStore002 can be found at: `http://localhost:3000/app/store/urn:ngsi-ld:Store:002`\n\n![Store](https://fiware.github.io/tutorials.Subscriptions/img/store2.png)\n\n\n# Architecture\n\nThis application will make use of only one FIWARE component - the [Orion Context Broker](https://catalogue.fiware.org/enablers/publishsubscribe-context-broker-orion-context-broker). Usage of the Orion Context Broker is sufficient for an application to qualify as *“Powered by FIWARE”*.\n\nCurrently, the Orion Context Broker relies on open source [MongoDB](https://www.mongodb.com/) 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 \n\n\nTherefore, the architecture will consist of four elements:\n\n* The Orion Context Broker server which will receive requests using [NGSI](https://swagger.lab.fiware.org/?url=https://raw.githubusercontent.com/Fiware/specifications/master/OpenAPI/ngsiv2/ngsiv2-openapi.json)\n* The underlying MongoDB database associated to the Orion Context Broker server\n* The Context Provider NGSI proxy which will will:\n  + receive requests using [NGSI](https://swagger.lab.fiware.org/?url=https://raw.githubusercontent.com/Fiware/specifications/master/OpenAPI/ngsiv2/ngsiv2-openapi.json)\n  + makes requests to publicly available data sources using their own APIs in a proprietory format \n  + returns context data back to the Orion Context Broker in [NGSI](https://swagger.lab.fiware.org/?url=https://raw.githubusercontent.com/Fiware/specifications/master/OpenAPI/ngsiv2/ngsiv2-openapi.json) format.\n* The Stock Management Frontend which will will:\n  + Display store information\n  + Show which products can be bought at each store\n  + Allow users to \"buy\" products and reduce the stock count.\n\nSince all interactions between the elements are initiated by HTTP requests, the entities can be containerized and run\nfrom exposed ports. \n\n![](https://fiware.github.io/tutorials.Subscriptions/img/architecture.png)\n\nThe necessary configuration information can be seen in the services section of the associated `docker-compose.yml` file. It\nhas been described in a [previous tutorial](https://github.com/Fiware/tutorials.Context-Providers/)\n\n# Prerequisites\n\n## Docker\n\nTo keep things simple both components will be run using [Docker](https://www.docker.com). **Docker** is a container technology\nwhich allows to different components isolated into their respective environments. \n\n* To install Docker on Windows follow the instructions [here](https://docs.docker.com/docker-for-windows/)\n* To install Docker on Mac follow the instructions [here](https://docs.docker.com/docker-for-mac/)\n* To install Docker on Linux follow the instructions [here](https://docs.docker.com/install/)\n\n**Docker Compose** is a tool for defining and running multi-container Docker applications. A \n[YAML file](https://raw.githubusercontent.com/Fiware/tutorials.Entity-Relationships/master/docker-compose.yml) 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  [here](https://docs.docker.com/compose/install/)\n\n## Cygwin \n\nWe will start up our services using a simple bash script. Windows users should download [cygwin](www.cygwin.com) to provide a\ncommand line functionality similar to a Linux distribution on Windows. \n\n# Start Up\n\nAll services can be initialised from the command line by running the bash script provided within the repository:\n\n```console\n./services create; ./services start;\n```\n\nThis command will also import seed data from the previous [Stock Management example](https://github.com/Fiware/tutorials.Context-Providers) on startup.\n\n>:information_source: **Note:** If you want to clean up and start over again you can do so with the following command:\n>\n>```console\n>./services stop\n>``` \n>\n\n#  Using Subscriptions\n\nTo follow the tutorial correctly please ensure you have the follow pages available on tabs in your browser before you enter any cUrl commands.\n\n#### Event Monitor\n\nThe event monitor can be found at: `http://localhost:3000/app/monitor`\n\n#### Stores\n\nThe stores can be found at:\n\n* Store 1 -  `http://localhost:3000/app/store/urn:ngsi-ld:Store:001`\n* Store 2 -  `http://localhost:3000/app/store/urn:ngsi-ld:Store:002`\n\n\n## Setting up a simple Subscription \n\nWithin the stock management example, imagine that the regional manager of the company wants to alter the price of a product. \nThe new price should immediately be reflected at the till in all stores within the system. It would be possible to set up the\nsystem so that it was constantly polling for new information, however prices are not changed very frequently so this would be\na waste of resources and create a lot of unnecessary data traffic.\n\nThe alternative is to create a subscription which will POST a payload to a \"well-known\" URL whenever a price has changed. \nA new subscription can be added by making a POST request to the `/v2/subscriptions/` endpoint as shown below:\n\n#### Request:\n\n```console\ncurl --request POST \\\n  --url 'http://{{orion}}/v2/subscriptions/' \\\n  --header 'content-type: application/json' \\\n  --data '{\n  \"description\": \"Notify me of all product price changes\",\n  \"subject\": {\n    \"entities\": [{\"idPattern\": \".*\", \"type\": \"Product\"}],\n    \"condition\": { \n      \"attrs\": [ \"price\" ]\n    }\n  },\n  \"notification\": {\n    \"http\": {\n      \"url\": \"http://context-provider:3000/subscription/price-change\"\n    }\n  }\n}'\n```\n\n\nThe body of the POST request consists of two parts, the `subject` section of the request (consisting of `entities` and `conditions`)states that the subscription will be fired whenever the `price` attribute of any **Product** entity is altered. The notification section of the body states that once the conditions of the subscription have been met, a POST request containing all affected **Product** entities will be sent to the URL `http://context-provider:3000/subscription/price-change` which is handled by the stock management front-end application.\n\nFor a first run, when the subscription is created, the Orion Context Broker runs the `condition` test, and since it has not been run before makes the assumption that all products have been changed. Therefore a request is sent to `subscription/price-change` immediately as shown:\n\n#### `http://localhost:3000/app/monitor`\n\n![](https://fiware.github.io/tutorials.Subscriptions/img/products-subscription.png)\n\nCode within the Stock Management Front-End application handles received the POST request as shown:\n\n```javascript\nrouter.post('/subscription/:type', (req, res) => {\n    _.forEach(req.body.data, item => {\n        broadcastEvents(req, item, ['refStore', 'refProduct', 'refShelf', 'type']);\n    });\n    res.status(204).send();\n});\n\nfunction broadcastEvents(req, item, types) {\n    const message = req.params.type + ' received';\n    _.forEach(types, type => {\n        if (item[type]) {\n            req.app.get('io').emit(item[type], message);\n        }\n    });\n}\n```\n\nThis business logic emits socket io events to any registered parties (such as the cash till)\n\nThe cash till has been set to reload if is receives an event - however in this case the prices have not changed yet, so the product prices\nremain the same - for example a bottle of beer remains at 0.99€\n\n#### `http://localhost:3000/app/store/urn:ngsi-ld:Store:002`\n\n![](https://fiware.github.io/tutorials.Subscriptions/img/beer-99.png)\n\nLet's reduce the price of a bottle of beer to 0.89€. This can't be done programmatically yet, so it has to be done with a curl command\nas shown:\n\n\n```console\ncurl --request PUT \\\n  --url 'http://{{orion}}/v2/entities/urn:ngsi-ld:Product:001/attrs/price/value' \\\n  --header 'Content-Type: text/plain' \\\n  --data 89\n```\n\nWhenever an attribute of the **Product** entity is updated, the Orion Context Broker checks for any existing subscriptions \n(which exist for that entity) and  applies the `condition` test. This time only one **Product** entity has changed since the last run therefore a POST request is sent to `subscription/price-change` - which only contains one **Product** in the body:\n\n#### `http://localhost:3000/app/monitor`\n\n![](https://fiware.github.io/tutorials.Subscriptions/img/price-change.png)\n\nThe business logic of the Stock Management Front End again emits socket io events to any registered parties (such as the cash till)\nand since the price has changed the till now displays a bottle of beer at 0.89€\n\n#### `http://localhost:3000/app/store/urn:ngsi-ld:Store:002`\n\n![](https://fiware.github.io/tutorials.Subscriptions/img/beer-89.png)   \n\n\n##  Reducing Payload with  `attrs` and `attrsFormat`\n\nWith the previous example the full verbose data from each affected **Product** entity was sent with the POST notification.\nThis is not very efficient. \n\nThe amount of data to passed can be reduced by adding an `attrs` attribute which will specify a list of attributes to be \nincluded in notification messages - other attributes are ignored\n\n>**Tip** an `exceptAttrs` attribute also exists to return all attributes except for those on the exclude list.\n> `attrs` and `exceptAttrs` cannot be used simualtaneously in the same subscription\n\n\nThe `attrsFormat` attribute specifies how the entities are represented in notifications. A verbose response is returned by \ndefault `keyValues` and `values` work in the same manner as a `v2/entities` GET request.\n\n##  Reducing Scope with  `expression` \n\nLets create two more subscriptions which will only fire under specific conditions - and will only return key-value pairs for\nthe entity affected. Imagine that the warehouse of each store now wants to be informed whenever the amount of product on the \nshelf falls below a threshold level. \n\nThe subscription is tested whenever the `shelfCount` of an **InventoryItem** is updated, however the addition of an `expression` \nattribute will mean that the subscription will only fire if the expression returns valid data - for example\n`\"q\": \"shelfCount<10;refStore==urn:ngsi-ld:Store:001` tests that the `shelfCount` is below ten and that the item is in store 001.\nThis means that we can set up out business logic so that other stores wont be bothered by notifications.\n\n\n\n#### Request:\n\nThe following command is a low stock notification for Store 001\n\n```console\ncurl --request POST \\\n  --url 'http://{{orion}}/v2/subscriptions' \\\n  --header 'Content-Type: application/json' \\\n  --data '{\n  \"description\": \"Notify me of low stock in Store 001\",\n  \"subject\": {\n    \"entities\": [{\"idPattern\": \".*\",\"type\": \"InventoryItem\"}],\n    \"condition\": {\n      \"attrs\": [\"shelfCount\"],\n      \"expression\": {\"q\": \"shelfCount<10;refStore==urn:ngsi-ld:Store:001\"}\n    }\n  },\n  \"notification\": {\n    \"http\": {\n      \"url\": \"http://context-provider:3000/subscription/low-stock-store001\"\n    },\n    \"attrsFormat\" : \"keyValues\"\n  }\n}'\n```\n\n\n#### Request:\n\nThe following command is a low stock notification for Store 002\n\n```console\ncurl --request POST \\\n  --url 'http://{{orion}}/v2/subscriptions' \\\n  --header 'Content-Type: application/json' \\\n  --data '{\n  \"description\": \"Notify me of low stock in Store 002\",\n  \"subject\": {\n    \"entities\": [{\"idPattern\": \".*\", \"type\": \"InventoryItem\"}],\n    \"condition\": {\n      \"attrs\": [\"shelfCount\"],\n      \"expression\": {\"q\": \"shelfCount<10;refStore==urn:ngsi-ld:Store:002\"}\n    }\n  },\n  \"notification\": {\n    \"http\": {\n      \"url\": \"http://context-provider:3000/subscription/low-stock-store002\"\n    },\n    \"attrsFormat\" : \"keyValues\"\n  }\n}'\n```\n\nThe two requests are very similiar. It is merely the `url` and the `expresssion` attributes which differ. The first cUrl command\nwill only fire if the affected **InventoryItem** entity has a reference to Store 001 and the second one if the affected\n**InventoryItem** entity has a reference to Store 001. Obviously the URLs must be different so that the business logic of our\napplication is able to react diffferently to each request.\n\n> **Tip**: You can set stock levels directly by making a PUT request as shown:\n>\n>```console\n>curl --request PUT \\\n>  --url 'http://{{orion}}/v2/entities/urn:ngsi-ld:InventoryItem:005/attrs/shelfCount/value' \\\n>  --header 'Content-Type: text/plain' \\\n>  --data 5\n>```\n\nIf you now buy items from Store 002, once an **InventoryItem** dips below ten items the following occurs\n\n#### `http://localhost:3000/app/monitor`\n![](https://fiware.github.io/tutorials.Subscriptions/img/low-stock-monitor.png)\n\nAs you can see the key value pairs of the affected  **InventoryItem**  have been passed to the Stock Managment Front End.\n\nIf you look at the store itself:\n\n#### `http://localhost:3000/app/store/urn:ngsi-ld:Store:002`\n![](https://fiware.github.io/tutorials.Subscriptions/img/low-stock-warehouse.png)\n\nAn alert has been raised by the business logic within the application.\n","schema":"https://schema.getpostman.com/json/collection/v2.0.0/collection.json","isPublicCollection":false,"owner":"513743","team":157450,"collectionId":"b240d096-0af8-44c3-9a5e-22fee62f49e7","publishedId":"RW1dHeTR","public":true,"publicUrl":"https://documenter-api.postman.tech/view/513743/RW1dHeTR","privateUrl":"https://go.postman.co/documentation/513743-b240d096-0af8-44c3-9a5e-22fee62f49e7","customColor":{"top-bar":"FFFFFF","right-sidebar":"4C4C4C","highlight":"233C68"},"documentationLayout":"classic-double-column","version":"8.10.1","publishDate":"2018-05-31T10:56:35.000Z","activeVersionTag":"latest","documentationTheme":"light","metaTags":{},"logos":{}},"statusCode":200},"environments":[],"user":{"authenticated":false,"permissions":{"publish":false}},"run":{"button":{"js":"https://run.pstmn.io/button.js","css":"https://run.pstmn.io/button.css"}},"web":"https://www.getpostman.com/","team":{"logo":"https://res.cloudinary.com/postman/image/upload/t_team_logo_pubdoc/v1/team/d7085d490b9144732c65203aa6e3b68b31884d1c33a86b8a00d15da75147ae33","favicon":""},"isEnvFetchError":false,"languages":"[{\"key\":\"csharp\",\"label\":\"C#\",\"variant\":\"HttpClient\"},{\"key\":\"csharp\",\"label\":\"C#\",\"variant\":\"RestSharp\"},{\"key\":\"curl\",\"label\":\"cURL\",\"variant\":\"cURL\"},{\"key\":\"dart\",\"label\":\"Dart\",\"variant\":\"http\"},{\"key\":\"go\",\"label\":\"Go\",\"variant\":\"Native\"},{\"key\":\"http\",\"label\":\"HTTP\",\"variant\":\"HTTP\"},{\"key\":\"java\",\"label\":\"Java\",\"variant\":\"OkHttp\"},{\"key\":\"java\",\"label\":\"Java\",\"variant\":\"Unirest\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"Fetch\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"jQuery\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"XHR\"},{\"key\":\"c\",\"label\":\"C\",\"variant\":\"libcurl\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Axios\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Native\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Request\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Unirest\"},{\"key\":\"objective-c\",\"label\":\"Objective-C\",\"variant\":\"NSURLSession\"},{\"key\":\"ocaml\",\"label\":\"OCaml\",\"variant\":\"Cohttp\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"cURL\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"Guzzle\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"HTTP_Request2\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"pecl_http\"},{\"key\":\"powershell\",\"label\":\"PowerShell\",\"variant\":\"RestMethod\"},{\"key\":\"python\",\"label\":\"Python\",\"variant\":\"http.client\"},{\"key\":\"python\",\"label\":\"Python\",\"variant\":\"Requests\"},{\"key\":\"r\",\"label\":\"R\",\"variant\":\"httr\"},{\"key\":\"r\",\"label\":\"R\",\"variant\":\"RCurl\"},{\"key\":\"ruby\",\"label\":\"Ruby\",\"variant\":\"Net::HTTP\"},{\"key\":\"shell\",\"label\":\"Shell\",\"variant\":\"Httpie\"},{\"key\":\"shell\",\"label\":\"Shell\",\"variant\":\"wget\"},{\"key\":\"swift\",\"label\":\"Swift\",\"variant\":\"URLSession\"}]","languageSettings":[{"key":"csharp","label":"C#","variant":"HttpClient"},{"key":"csharp","label":"C#","variant":"RestSharp"},{"key":"curl","label":"cURL","variant":"cURL"},{"key":"dart","label":"Dart","variant":"http"},{"key":"go","label":"Go","variant":"Native"},{"key":"http","label":"HTTP","variant":"HTTP"},{"key":"java","label":"Java","variant":"OkHttp"},{"key":"java","label":"Java","variant":"Unirest"},{"key":"javascript","label":"JavaScript","variant":"Fetch"},{"key":"javascript","label":"JavaScript","variant":"jQuery"},{"key":"javascript","label":"JavaScript","variant":"XHR"},{"key":"c","label":"C","variant":"libcurl"},{"key":"nodejs","label":"NodeJs","variant":"Axios"},{"key":"nodejs","label":"NodeJs","variant":"Native"},{"key":"nodejs","label":"NodeJs","variant":"Request"},{"key":"nodejs","label":"NodeJs","variant":"Unirest"},{"key":"objective-c","label":"Objective-C","variant":"NSURLSession"},{"key":"ocaml","label":"OCaml","variant":"Cohttp"},{"key":"php","label":"PHP","variant":"cURL"},{"key":"php","label":"PHP","variant":"Guzzle"},{"key":"php","label":"PHP","variant":"HTTP_Request2"},{"key":"php","label":"PHP","variant":"pecl_http"},{"key":"powershell","label":"PowerShell","variant":"RestMethod"},{"key":"python","label":"Python","variant":"http.client"},{"key":"python","label":"Python","variant":"Requests"},{"key":"r","label":"R","variant":"httr"},{"key":"r","label":"R","variant":"RCurl"},{"key":"ruby","label":"Ruby","variant":"Net::HTTP"},{"key":"shell","label":"Shell","variant":"Httpie"},{"key":"shell","label":"Shell","variant":"wget"},{"key":"swift","label":"Swift","variant":"URLSession"}],"languageOptions":[{"label":"C# - HttpClient","value":"csharp - HttpClient - C#"},{"label":"C# - RestSharp","value":"csharp - RestSharp - C#"},{"label":"cURL - cURL","value":"curl - cURL - cURL"},{"label":"Dart - http","value":"dart - http - Dart"},{"label":"Go - Native","value":"go - Native - Go"},{"label":"HTTP - HTTP","value":"http - HTTP - HTTP"},{"label":"Java - OkHttp","value":"java - OkHttp - Java"},{"label":"Java - Unirest","value":"java - Unirest - Java"},{"label":"JavaScript - Fetch","value":"javascript - Fetch - JavaScript"},{"label":"JavaScript - jQuery","value":"javascript - jQuery - JavaScript"},{"label":"JavaScript - XHR","value":"javascript - XHR - JavaScript"},{"label":"C - libcurl","value":"c - libcurl - C"},{"label":"NodeJs - Axios","value":"nodejs - Axios - NodeJs"},{"label":"NodeJs - Native","value":"nodejs - Native - NodeJs"},{"label":"NodeJs - Request","value":"nodejs - Request - NodeJs"},{"label":"NodeJs - Unirest","value":"nodejs - Unirest - NodeJs"},{"label":"Objective-C - NSURLSession","value":"objective-c - NSURLSession - Objective-C"},{"label":"OCaml - Cohttp","value":"ocaml - Cohttp - OCaml"},{"label":"PHP - cURL","value":"php - cURL - PHP"},{"label":"PHP - Guzzle","value":"php - Guzzle - PHP"},{"label":"PHP - HTTP_Request2","value":"php - HTTP_Request2 - PHP"},{"label":"PHP - pecl_http","value":"php - pecl_http - PHP"},{"label":"PowerShell - RestMethod","value":"powershell - RestMethod - PowerShell"},{"label":"Python - http.client","value":"python - http.client - Python"},{"label":"Python - Requests","value":"python - Requests - Python"},{"label":"R - httr","value":"r - httr - R"},{"label":"R - RCurl","value":"r - RCurl - R"},{"label":"Ruby - Net::HTTP","value":"ruby - Net::HTTP - Ruby"},{"label":"Shell - Httpie","value":"shell - Httpie - Shell"},{"label":"Shell - wget","value":"shell - wget - Shell"},{"label":"Swift - URLSession","value":"swift - URLSession - Swift"}],"layoutOptions":[{"value":"classic-single-column","label":"Single Column"},{"value":"classic-double-column","label":"Double Column"}],"versionOptions":[],"environmentOptions":[{"value":"0","label":"No Environment"}],"canonicalUrl":"https://documenter.gw.postman.com/view/metadata/RW1dHeTR"}