diff --git a/.gitignore b/.gitignore index 550b62f43..30ad3184b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ node_modules /content/influxdb/*/api/*.html /api-docs/redoc-static.html* .vscode/* +.idea +package-lock.json \ No newline at end of file diff --git a/content/influxdb/cloud/api-guide/tutorials/_index.md b/content/influxdb/cloud/api-guide/tutorials/_index.md new file mode 100644 index 000000000..5e09b82ad --- /dev/null +++ b/content/influxdb/cloud/api-guide/tutorials/_index.md @@ -0,0 +1,13 @@ +--- +title: InfluxDB API client library tutorials +seotitle: Get started with InfluxDB API client libraries +description: Follow step-by-step tutorials to for InfluxDB API client libraries in your favorite framework or language. +weight: 4 +menu: + influxdb_cloud: + name: Client library tutorials + parent: Develop with the API +influxdb/cloud/tags: [api] +--- + +{{< children >}} diff --git a/content/influxdb/cloud/api-guide/tutorials/client-library-starter/_index.md b/content/influxdb/cloud/api-guide/tutorials/client-library-starter/_index.md new file mode 100644 index 000000000..ff3308178 --- /dev/null +++ b/content/influxdb/cloud/api-guide/tutorials/client-library-starter/_index.md @@ -0,0 +1,13 @@ +--- +title: InfluxDB API client library starter +seotitle: Starter tutorial for InfluxDB API client libraries +description: Follow step-by-step tutorials to build an IoT dashboard with API client libraries in your favorite framework or language. +weight: 4 +menu: + influxdb_cloud: + name: Client library starter + parent: Client library tutorials +influxdb/cloud/tags: [api] +--- + +{{% duplicate-oss %}} diff --git a/content/influxdb/cloud/api-guide/tutorials/client-library-starter/nodejs.md b/content/influxdb/cloud/api-guide/tutorials/client-library-starter/nodejs.md new file mode 100644 index 000000000..89881eb9d --- /dev/null +++ b/content/influxdb/cloud/api-guide/tutorials/client-library-starter/nodejs.md @@ -0,0 +1,16 @@ +--- +title: JavaScript client library starter +seotitle: Use JavaScript client library to build a sample application +list_title: JavaScript client library starter +description: > + Build a JavaScript application that writes, queries, and manages devices with the + InfluxDB client library. +menu: + influxdb_cloud: + identifier: client-library-starter-js + name: JavaScript + parent: Client library starter +influxdb/cloud/tags: [api, javascript, nodejs] +--- + +{{% duplicate-oss %}} diff --git a/content/influxdb/cloud/api-guide/tutorials/client-library-starter/python.md b/content/influxdb/cloud/api-guide/tutorials/client-library-starter/python.md new file mode 100644 index 000000000..683360a54 --- /dev/null +++ b/content/influxdb/cloud/api-guide/tutorials/client-library-starter/python.md @@ -0,0 +1,17 @@ +--- +title: Python client library starter +seotitle: Use Python client library to build a sample application +list_title: Python +description: > + Build an application that writes, queries, and manages devices with the InfluxDB + client library for Python. +weight: 3 +menu: + influxdb_cloud: + identifier: client-library-starter-py + name: Python + parent: Client library starter +influxdb/cloud/tags: [api, python] +--- + +{{% duplicate-oss %}} diff --git a/content/influxdb/v2.2/api-guide/_index.md b/content/influxdb/v2.2/api-guide/_index.md index 4aa736384..47e1d394e 100644 --- a/content/influxdb/v2.2/api-guide/_index.md +++ b/content/influxdb/v2.2/api-guide/_index.md @@ -12,20 +12,28 @@ influxdb/v2.2/tags: [api] The InfluxDB v2 API provides a programmatic interface for interactions with InfluxDB. Access the InfluxDB API using the `/api/v2/` endpoint. +## Developer guides + +- [API starter guide](/influxdb/v2.2/api-guide/starter/) + ## InfluxDB client libraries + InfluxDB client libraries are language-specific packages that integrate with the InfluxDB v2 API. -For information about supported client libraries, see [InfluxDB client libraries](/{{< latest "influxdb" >}}/api-guide/client-libraries/). +For tutorials and information about client libraries, see [InfluxDB client libraries](/{{< latest "influxdb" >}}/api-guide/client-libraries/). ## InfluxDB v2 API documentation + InfluxDB OSS {{< current-version >}} API documentation -#### View InfluxDB API documentation locally +### View InfluxDB API documentation locally + InfluxDB API documentation is built into the `influxd` service and represents the API specific to the current version of InfluxDB. To view the API documentation locally, [start InfluxDB](/influxdb/v2.2/get-started/#start-influxdb) and visit the `/docs` endpoint in a browser ([localhost:8086/docs](http://localhost:8086/docs)). ## InfluxDB v1 compatibility API documentation + The InfluxDB v2 API includes [InfluxDB 1.x compatibility endpoints](/influxdb/v2.2/reference/api/influxdb-1x/) that work with InfluxDB 1.x client libraries and third-party integrations like [Grafana](https://grafana.com) and others. diff --git a/content/influxdb/v2.2/api-guide/api_intro.md b/content/influxdb/v2.2/api-guide/api_intro.md index 3d59e4382..acef91942 100644 --- a/content/influxdb/v2.2/api-guide/api_intro.md +++ b/content/influxdb/v2.2/api-guide/api_intro.md @@ -1,11 +1,11 @@ --- title: API Quick Start seotitle: Use the InfluxDB API -description: Interact with InfluxDB 1 using a rich API for writing and querying data and more. +description: Interact with InfluxDB using a rich API for writing and querying data and more. weight: 3 menu: influxdb_2_2: - name: Quick Start + name: Quick start parent: Develop with the API aliases: - /influxdb/v2.2/tools/api/ diff --git a/content/influxdb/v2.2/api-guide/tutorials/_index.md b/content/influxdb/v2.2/api-guide/tutorials/_index.md new file mode 100644 index 000000000..55856b4a7 --- /dev/null +++ b/content/influxdb/v2.2/api-guide/tutorials/_index.md @@ -0,0 +1,13 @@ +--- +title: InfluxDB API client library tutorials +seotitle: Get started with InfluxDB API client libraries +description: Follow step-by-step tutorials to for InfluxDB API client libraries in your favorite framework or language. +weight: 4 +menu: + influxdb_2_2: + name: Client library tutorials + parent: Develop with the API +influxdb/v2.2/tags: [api] +--- + +{{< children >}} diff --git a/content/influxdb/v2.2/api-guide/tutorials/client-library-starter/_index.md b/content/influxdb/v2.2/api-guide/tutorials/client-library-starter/_index.md new file mode 100644 index 000000000..5444176ea --- /dev/null +++ b/content/influxdb/v2.2/api-guide/tutorials/client-library-starter/_index.md @@ -0,0 +1,30 @@ +--- +title: InfluxDB API client library starter +seotitle: Starter tutorial for InfluxDB API client libraries +description: Follow step-by-step tutorials to build an IoT dashboard with API client libraries in your favorite framework or language. +weight: 4 +menu: + influxdb_2_2: + name: Client library starter + parent: Client library tutorials +influxdb/v2.2/tags: [api] +--- + +Follow step-by-step tutorials to build an Internet-of-Things (IoT) application with InfluxData client libraries and your favorite framework or language. +InfluxData and the user community maintain client libraries for developers who want to take advantage of: + +- Idioms for InfluxDB requests, responses, and errors. +- Common patterns in a familiar programming language. +- Faster development and less boilerplate code. + +These tutorials walk through using the InfluxDB API and +client libraries to build a modern application as you learn the following: + +- InfluxDB core concepts. +- How the application interacts with devices and InfluxDB. +- How to authenticate apps and devices to the API. +- How to install a client library. +- How to write and query data in InfluxDB. +- How to use the InfluxData UI libraries to format data and create visualizations. + +{{< children >}} diff --git a/content/influxdb/v2.2/api-guide/tutorials/client-library-starter/nodejs.md b/content/influxdb/v2.2/api-guide/tutorials/client-library-starter/nodejs.md new file mode 100644 index 000000000..2b617c688 --- /dev/null +++ b/content/influxdb/v2.2/api-guide/tutorials/client-library-starter/nodejs.md @@ -0,0 +1,508 @@ +--- +title: JavaScript client library starter +seotitle: Use JavaScript client library to build a sample application +list_title: JavaScript client library starter +description: > + Build a JavaScript application that writes, queries, and manages devices with the + InfluxDB client library. +menu: + influxdb_2_2: + identifier: client-library-starter-js + name: JavaScript + parent: Client library starter +influxdb/v2.2/tags: [api, javascript, nodejs] +--- + +{{% api/iot-starter-intro %}} + +## Contents + +- [Contents](#contents) +- [Set up InfluxDB](#set-up-influxdb) + - [Authenticate with an InfluxDB API token](#authenticate-with-an-influxdb-api-token) +- [Introducing IoT Starter](#introducing-iot-starter) +- [Create the application](#create-the-application) +- [Install InfluxDB client library](#install-influxdb-client-library) +- [Configure the client library](#configure-the-client-library) +- [Build the API](#build-the-api) +- [Create the API to list devices](#create-the-api-to-list-devices) + - [Handle requests for device information](#handle-requests-for-device-information) + - [Retrieve and list devices](#retrieve-and-list-devices) +- [Create the API to register devices](#create-the-api-to-register-devices) + - [Create an authorization for the device](#create-an-authorization-for-the-device) + - [Write the device authorization to a bucket](#write-the-device-authorization-to-a-bucket) +- [Install and run the UI](#install-and-run-the-ui) + +## Set up InfluxDB + +If you haven't already, [create an InfluxDB Cloud account](https://www.influxdata.com/products/influxdb-cloud/) or [install InfluxDB OSS](https://www.influxdata.com/products/influxdb/). + +### Authenticate with an InfluxDB API token + +For convenience in development, +[create an _All-Access_ token](/influxdb/v2.2/security/tokens/create-token/) +for your application. This grants your application full read and write +permissions on all resources within your InfluxDB organization. + +{{% note %}} + +For a production application, create and use a +{{% cloud-only %}}custom{{% /cloud-only %}}{{% oss-only %}}read-write{{% /oss-only %}} +token with minimal permissions and only use it with your application. + +{{% /note %}} + +## Introducing IoT Starter + +The application architecture has four layers: + +- **InfluxDB API**: InfluxDB v2 API. +- **IoT device**: Virtual or physical devices write IoT data to the InfluxDB API. +- **UI**: Sends requests to the server and renders views in the browser. +- **API**: Receives requests from the UI, sends requests to InfluxDB, and processes responses from InfluxDB. + +{{% note %}} +For the complete code referenced in this tutorial, see the [influxdata/iot-api-js repository](https://github.com/influxdata/iot-api-js). +{{% /note %}} + +## Create the application + +Create a directory that will contain your `iot-api` projects. +The following example code creates an `iot-api` directory in your home directory +and changes to the new directory: + +```bash +mkdir ~/iot-api-apps +cd ~/iot-api-apps +``` + +Use [Next.js](https://nextjs.org/), a framework for full-stack JavaScript applications, to create your application. + +1. In your `~/iot-api-apps` directory, open a terminal and enter the following commands to create the `iot-api-js` app from the NextJS [learn-starter template](https://github.com/vercel/next-learn/tree/master/basics/learn-starter): + + ```bash + npx create-next-app iot-api-js --use-npm --example "https://github.com/vercel/next-learn/tree/master/basics/learn-starter" + ``` + +2. After the installation completes, enter the following commands in your terminal to go into your `./iot-api-js` directory and start the development server: + + ```bash + cd iot-api-js + npm run dev -p 3001 + ``` + +To view the application, visit in your browser. + +## Install InfluxDB client library + +The InfluxDB client library provides the following InfluxDB API interactions: + +- Query data with the Flux language. +- Write data to InfluxDB. +- Batch data in the background. +- Retry requests automatically on failure. + +1. Enter the following command into your terminal to install the client library: + + ```bash + npm i @influxdata/influxdb-client + ``` + +2. Enter the following command into your terminal to install `@influxdata/influxdb-client-apis`, the _management APIs_ that create, modify, and delete authorizations, buckets, tasks, and other InfluxDB resources: + + ```bash + npm i @influxdata/influxdb-client-apis + ``` + +For more information about the client library, see the [influxdata/influxdb-client-js repo](https://github.com/influxdata/influxdb-client-js). + +## Configure the client library + +InfluxDB client libraries require configuration properties from your InfluxDB environment. +Typically, you'll provide the following properties as environment variables for your application: + +- `INFLUX_URL` +- `INFLUX_TOKEN` +- `INFLUX_ORG` +- `INFLUX_BUCKET` +- `INFLUX_BUCKET_AUTH` + +Next.js uses the `env` module to provide environment variables to your application. + +The `./.env.development` file is versioned and contains non-secret default settings for your _development_ environment. + +```bash +# .env.development + +INFLUX_URL=http://localhost:8086 +INFLUX_BUCKET=iot_center +INFLUX_BUCKET_AUTH=iot_center_devices +``` + +To configure secrets and settings that aren't added to version control, +create a `./.env.local` file and set the variables--for example, set your InfluxDB token and organization: + +```sh +# .env.local + +# INFLUX_TOKEN +# InfluxDB API token used by the application server to send requests to InfluxDB. +# For convenience in development, use an **All-Access** token. + +INFLUX_TOKEN=29Xx1KH9VkASPR2DSfRfFd82OwGD... + +# INFLUX_ORG +# InfluxDB organization ID you want to use in development. + +INFLUX_ORG=48c88459ee424a04 +``` + +Enter the following commands into your terminal to restart and load the `.env` files: + + 1. `CONTROL+C` to stop the application. + 2. `npm run dev` to start the application. + +Next.js sets variables that you can access in the `process.env` object--for example: + +```ts +console.log(process.env.INFLUX_ORG) +``` + +## Build the API + +Your application API provides server-side HTTP endpoints that process requests from the UI. +Each API endpoint is responsible for the following: + +1. Listen for HTTP requests (from the UI). +2. Translate requests into InfluxDB API requests. +3. Process InfluxDB API responses and handle errors. +4. Respond with status and data (for the UI). + +## Create the API to list devices + +Add the `/api/devices` API endpoint that retrieves, processes, and lists devices. +`/api/devices` uses the `/api/v2/query` InfluxDB API endpoint to query `INFLUX_BUCKET_AUTH` for a registered device. + +### Handle requests for device information + +1. Create a `./pages/api/devices/[[...deviceParams]].js` file to handle requests for `/api/devices` and `/api/devices//measurements/`. + +2. In the file, export a Next.js request `handler` function. +[See the example](https://github.com/influxdata/iot-api-js/blob/18d34bcd59b93ad545c5cd9311164c77f6d1995a/pages/api/devices/%5B%5B...deviceParams%5D%5D.js). + + {{% note %}} +In Next.js, the filename pattern `[[...param]].js` creates a _catch-all_ API route. +To learn more, see [Next.js dynamic API routes](https://nextjs.org/docs/api-routes/dynamic-api-routes). + {{% /note %}} + +### Retrieve and list devices + +Retrieve registered devices in `INFLUX_BUCKET_AUTH` and process the query results. + +1. Create a Flux query that gets the last row of each [series](/influxdb/v2.2/reference/glossary#series) that contains a `deviceauth` measurement. + The example query below returns rows that contain the `key` field (authorization ID) and excludes rows that contain a `token` field (to avoid exposing tokens to the UI). + + ```js + // Flux query finds devices + from(bucket:`${INFLUX_BUCKET_AUTH}`) + |> range(start: 0) + |> filter(fn: (r) => r._measurement == "deviceauth" and r._field != "token") + |> last() + ``` + +2. Use the `QueryApi` client to send the Flux query to the `POST /api/v2/query` InfluxDB API endpoint. + +Create a `./pages/api/devices/_devices.js` file that contains the following: + +{{< code-tabs-wrapper >}} +{{% code-tabs %}} +[Node.js](#nodejs) +{{% /code-tabs %}} +{{% code-tab-content %}} + +{{% truncate %}} + +```ts +import { InfluxDB } from '@influxdata/influxdb-client' +import { flux } from '@influxdata/influxdb-client' + +const INFLUX_ORG = process.env.INFLUX_ORG +const INFLUX_BUCKET_AUTH = process.env.INFLUX_BUCKET_AUTH +const influxdb = new InfluxDB({url: process.env.INFLUX_URL, token: process.env.INFLUX_TOKEN}) + +/** + * Gets devices or a particular device when deviceId is specified. Tokens + * are not returned unless deviceId is specified. It can also return devices + * with empty/unknown key, such devices can be ignored (InfluxDB authorization is not associated). + * @param deviceId optional deviceId + * @returns promise with an Record. + */ + export async function getDevices(deviceId) { + const queryApi = influxdb.getQueryApi(INFLUX_ORG) + const deviceFilter = + deviceId !== undefined + ? flux` and r.deviceId == "${deviceId}"` + : flux` and r._field != "token"` + const fluxQuery = flux`from(bucket:${INFLUX_BUCKET_AUTH}) + |> range(start: 0) + |> filter(fn: (r) => r._measurement == "deviceauth"${deviceFilter}) + |> last()` + const devices = {} + + return await new Promise((resolve, reject) => { + queryApi.queryRows(fluxQuery, { + next(row, tableMeta) { + const o = tableMeta.toObject(row) + const deviceId = o.deviceId + if (!deviceId) { + return + } + const device = devices[deviceId] || (devices[deviceId] = {deviceId}) + device[o._field] = o._value + if (!device.updatedAt || device.updatedAt < o._time) { + device.updatedAt = o._time + } + }, + error: reject, + complete() { + resolve(devices) + }, + }) + }) +} +``` + +{{% /truncate %}} + +{{% caption %}}[iot-api-js/pages/api/devices/_devices.js getDevices(deviceId)](https://github.com/influxdata/iot-api-js/blob/18d34bcd59b93ad545c5cd9311164c77f6d1995a/pages/api/devices/_devices.js){{% /caption %}} + +{{% /code-tab-content %}} +{{< /code-tabs-wrapper >}} + +The `_devices` module exports a `getDevices(deviceId)` function that queries +for registered devices, processes the data, and returns a Promise with the result. +If you invoke the function as `getDevices()` (without a _`deviceId`_), +it retrieves all `deviceauth` points and returns a Promise with `{ DEVICE_ID: ROW_DATA }`. + +To send the query and process results, the `getDevices(deviceId)` function uses the `QueryAPI queryRows(query, consumer)` method. +`queryRows` executes the `query` and provides the Annotated CSV result as an Observable to the `consumer`. +`queryRows` has the following TypeScript signature: + +```ts +queryRows( + query: string | ParameterizedQuery, + consumer: FluxResultObserver +): void +``` + +{{% caption %}}[@influxdata/influxdb-client-js QueryAPI](https://github.com/influxdata/influxdb-client-js/blob/3db2942432b993048d152e0d0e8ec8499eedfa60/packages/core/src/QueryApi.ts){{% /caption %}} + +The `consumer` that you provide must implement the [`FluxResultObserver` interface](https://github.com/influxdata/influxdb-client-js/blob/3db2942432b993048d152e0d0e8ec8499eedfa60/packages/core/src/results/FluxResultObserver.ts) and provide the following callback functions: + +- `next(row, tableMeta)`: processes the next row and table metadata--for example, to prepare the response. +- `error(error)`: receives and handles errors--for example, by rejecting the Promise. +- `complete()`: signals when all rows have been consumed--for example, by resolving the Promise. + +To learn more about Observers, see the [RxJS Guide](https://rxjs.dev/guide/observer). + +## Create the API to register devices + +In this application, a _registered device_ is a point that contains your device ID, authorization ID, and API token. +The API token and authorization permissions allow the device to query and write to `INFLUX_BUCKET`. +In this section, you add the API endpoint that handles requests from the UI, creates an authorization in InfluxDB, +and writes the registered device to the `INFLUX_BUCKET_AUTH` bucket. +To learn more about API tokens and authorizations, see [Manage API tokens](/influxdb/v2.2/security/tokens/) + +The application API uses the following `/api/v2` InfluxDB API endpoints: + +- `POST /api/v2/query`: to query `INFLUX_BUCKET_AUTH` for a registered device. +- `GET /api/v2/buckets`: to get the bucket ID for `INFLUX_BUCKET`. +- `POST /api/v2/authorizations`: to create an authorization for the device. +- `POST /api/v2/write`: to write the device authorization to `INFLUX_BUCKET_AUTH`. + +1. Add a `./pages/api/devices/create.js` file to handle requests for `/api/devices/create`. +2. In the file, export a Next.js request `handler` function that does the following: + + 1. Accept a device ID in the request body. + 2. Query `INFLUX_BUCKET_AUTH` and respond with an error if an authorization exists for the device. + 3. [Create an authorization for the device](#create-an-authorization-for-the-device). + 4. [Write the device ID and authorization to `INFLUX_BUCKET_AUTH`](#write-the-device-authorization-to-a-bucket). + 5. Respond with `HTTP 200` when the write request completes. + +[See the example](https://github.com/influxdata/iot-api-js/blob/25b38c94a1f04ea71f2ef4b9fcba5350d691cb9d/pages/api/devices/create.js). + +### Create an authorization for the device + +In this section, you create an authorization with _read_-_write_ permission to `INFLUX_BUCKET` and receive an API token for the device. +The example below uses the following steps to create the authorization: + +1. Instantiate the `AuthorizationsAPI` client and `BucketsAPI` client with the configuration. +2. Retrieve the bucket ID. +3. Use the client library to send a `POST` request to the `/api/v2/authorizations` InfluxDB API endpoint. + +In `./api/devices/create.js`, add the following `createAuthorization(deviceId)` function: + +{{< code-tabs-wrapper >}} +{{% code-tabs %}} +[Node.js](#nodejs) +{{% /code-tabs %}} +{{% code-tab-content %}} + +{{% truncate %}} + +```ts +import { InfluxDB } from '@influxdata/influxdb-client' +import { getDevices } from './_devices' +import { AuthorizationsAPI, BucketsAPI } from '@influxdata/influxdb-client-apis' +import { Point } from '@influxdata/influxdb-client' + +const INFLUX_ORG = process.env.INFLUX_ORG +const INFLUX_BUCKET_AUTH = process.env.INFLUX_BUCKET_AUTH +const INFLUX_BUCKET = process.env.INFLUX_BUCKET + +const influxdb = new InfluxDB({url: process.env.INFLUX_URL, token: process.env.INFLUX_TOKEN}) + +/** + * Creates an authorization for a supplied deviceId + * @param {string} deviceId client identifier + * @returns {import('@influxdata/influxdb-client-apis').Authorization} promise with authorization or an error + */ +async function createAuthorization(deviceId) { + const authorizationsAPI = new AuthorizationsAPI(influxdb) + const bucketsAPI = new BucketsAPI(influxdb) + const DESC_PREFIX = 'IoTCenterDevice: ' + + const buckets = await bucketsAPI.getBuckets({name: INFLUX_BUCKET, orgID: INFLUX_ORG}) + const bucketId = buckets.buckets[0]?.id + + return await authorizationsAPI.postAuthorizations({ + body: { + orgID: INFLUX_ORG, + description: DESC_PREFIX + deviceId, + permissions: [ + { + action: 'read', + resource: {type: 'buckets', id: bucketId, orgID: INFLUX_ORG}, + }, + { + action: 'write', + resource: {type: 'buckets', id: bucketId, orgID: INFLUX_ORG}, + }, + ], + }, + }) +} +``` + +{{% /truncate %}} +{{% caption %}}[iot-api-js/pages/api/devices/create.js](https://github.com/influxdata/iot-api-js/blob/42a37d683b5e4df601422f85d2c22f5e9d592e68/pages/api/devices/create.js){{% /caption %}} + +{{% /code-tab-content %}} +{{< /code-tabs-wrapper >}} + +To create an authorization that has _read_-_write_ permission to `INFLUX_BUCKET`, you need the bucket ID. +To retrieve the bucket ID, +`createAuthorization(deviceId)` calls the `BucketsAPI getBuckets` function that sends a `GET` request to +the `/api/v2/buckets` InfluxDB API endpoint. +`createAuthorization(deviceId)` then passes a new authorization in the request body with the following: + +- Bucket ID. +- Organization ID. +- Description: `IoTCenterDevice: DEVICE_ID`. +- List of permissions to the bucket. + +To learn more about API tokens and authorizations, see [Manage API tokens](/influxdb/v2.2/security/tokens/). + +Next, [write the device authorization to a bucket](#write-the-device-authorization-to-a-bucket). + +### Write the device authorization to a bucket + +With a device authorization in InfluxDB, write a point for the device and authorization details to `INFLUX_BUCKET_AUTH`. +Storing the device authorization in a bucket allows you to do the following: + +- Report device authorization history. +- Manage devices with and without tokens. +- Assign the same token to multiple devices. +- Refresh tokens. + +To write a point to InfluxDB, use the InfluxDB client library to send a `POST` request to the `/api/v2/write` InfluxDB API endpoint. +In `./pages/api/devices/create.js`, add the following `createDevice(deviceId)` function: + +{{< code-tabs-wrapper >}} +{{% code-tabs %}} +[Node.js](#nodejs) +{{% /code-tabs %}} +{{% code-tab-content %}} + +```ts +/** Creates an authorization for a deviceId and writes it to a bucket */ +async function createDevice(deviceId) { + let device = (await getDevices(deviceId)) || {} + let authorizationValid = !!Object.values(device)[0]?.key + if(authorizationValid) { + console.log(JSON.stringify(device)) + return Promise.reject('This device ID is already registered and has an authorization.') + } else { + console.log(`createDeviceAuthorization: deviceId=${deviceId}`) + const authorization = await createAuthorization(deviceId) + const writeApi = influxdb.getWriteApi(INFLUX_ORG, INFLUX_BUCKET_AUTH, 'ms', { + batchSize: 2, + }) + const point = new Point('deviceauth') + .tag('deviceId', deviceId) + .stringField('key', authorization.id) + .stringField('token', authorization.token) + writeApi.writePoint(point) + await writeApi.close() + return + } +} +``` + +{{% caption %}}[iot-api-js/pages/api/devices/create.js](https://github.com/influxdata/iot-api-js/blob/25b38c94a1f04ea71f2ef4b9fcba5350d691cb9d/pages/api/devices/create.js){{% /caption %}} + +{{% /code-tab-content %}} +{{< /code-tabs-wrapper >}} + +`createDevice(device_id)` takes a _`device_id`_ and writes data to `INFLUX_BUCKET_AUTH` in the following steps: + +1. Initialize `InfluxDBClient()` with `url`, `token`, and `org` values from the configuration. +2. Initialize a `WriteAPI` client for writing data to an InfluxDB bucket. +3. Create a `Point`. +4. Use `writeApi.writePoint(point)` to write the `Point` to the bucket. + +The function writes a point with the following elements: + +| Element | Name | Value | +|:------------|:-----------|:--------------------------| +| measurement | | `deviceauth` | +| tag | `deviceId` | device ID | +| field | `key` | authorization ID | +| field | `token` | authorization (API) token | + +## Install and run the UI + +`influxdata/iot-api-ui` is a standalone [Next.js React](https://nextjs.org/docs/basic-features/pages) UI that uses your application API to write and query data in InfluxDB. +`iot-api-ui` uses Next.js _[rewrites](https://nextjs.org/docs/api-reference/next.config.js/rewrites)_ to route all requests in the `/api/` path to your API. + +To install and run the UI, do the following: + +1. In your `~/iot-api-apps` directory, clone the [`influxdata/iot-api-ui` repo](https://github.com/influxdata/iot-api-ui) and go into the `iot-api-ui` directory--for example: + + ```bash + cd ~/iot-api-apps + git clone git@github.com:influxdata/iot-api-ui.git + cd ./iot-app-ui + ``` + +2. The `./.env.development` file contains default configuration settings that you can + edit or override (with a `./.env.local` file). +3. To start the UI, enter the following command into your terminal: + + ```bash + yarn dev + ``` + + To view the list and register devices, visit in your browser. + +To learn more about the UI components, see [`influxdata/iot-api-ui`](https://github.com/influxdata/iot-api-ui). diff --git a/content/influxdb/v2.2/api-guide/tutorials/client-library-starter/python.md b/content/influxdb/v2.2/api-guide/tutorials/client-library-starter/python.md new file mode 100644 index 000000000..a735a3bb9 --- /dev/null +++ b/content/influxdb/v2.2/api-guide/tutorials/client-library-starter/python.md @@ -0,0 +1,583 @@ +--- +title: Python client library starter +seotitle: Use Python client library to build a sample application +list_title: Python +description: > + Build an application that writes, queries, and manages devices with the InfluxDB + client library for Python. +weight: 3 +menu: + influxdb_2_2: + identifier: client-library-starter-py + name: Python + parent: Client library starter +influxdb/v2.2/tags: [api, python] +--- + +{{% api/iot-starter-intro %}} +- How to use the InfluxData UI libraries to format data and create visualizations. + +## Contents + +- [Contents](#contents) +- [Set up InfluxDB](#set-up-influxdb) + - [Authenticate with an InfluxDB API token](#authenticate-with-an-influxdb-api-token) +- [Introducing IoT Starter](#introducing-iot-starter) +- [Create the application](#create-the-application) +- [Install InfluxDB client library](#install-influxdb-client-library) +- [Configure the client library](#configure-the-client-library) +- [Build the API](#build-the-api) +- [Create the API to register devices](#create-the-api-to-register-devices) + - [Create an authorization for the device](#create-an-authorization-for-the-device) + - [Write the device authorization to a bucket](#write-the-device-authorization-to-a-bucket) +- [Create the API to list devices](#create-the-api-to-list-devices) +- [Create IoT virtual device](#create-iot-virtual-device) +- [Write telemetry data](#write-telemetry-data) +- [Query telemetry data](#query-telemetry-data) +- [Define API responses](#define-api-responses) +- [Install and run the UI](#install-and-run-the-ui) + +## Set up InfluxDB + +If you haven't already, [create an InfluxDB Cloud account](https://www.influxdata.com/products/influxdb-cloud/) or [install InfluxDB OSS](https://www.influxdata.com/products/influxdb/). + +### Authenticate with an InfluxDB API token + +For convenience in development, +[create an _All-Access_ token](/influxdb/v2.2/security/tokens/create-token/) +for your application. This grants your application full read and write +permissions on all resources within your InfluxDB organization. + +{{% note %}} + +For a production application, create and use a +{{% cloud-only %}}custom{{% /cloud-only %}}{{% oss-only %}}read-write{{% /oss-only %}} +token with minimal permissions and only use it with your application. + +{{% /note %}} + +## Introducing IoT Starter + +The application architecture has four layers: + +- **InfluxDB API**: InfluxDB v2 API. +- **IoT device**: Virtual or physical devices write IoT data to the InfluxDB API. +- **UI**: Sends requests to the server and renders views in the browser. +- **API**: Receives requests from the UI, sends requests to InfluxDB, + and processes responses from InfluxDB. + +{{% note %}} +For the complete code referenced in this tutorial, see the [influxdata/iot-api-python repository](https://github.com/influxdata/iot-api-python). +{{% /note %}} + +## Create the application + +Create a directory that will contain your `iot-api` projects. +The following example code creates an `iot-api` directory in your home directory +and changes to the new directory: + +```bash +mkdir ~/iot-api-apps +cd ~/iot-api-apps +``` + +Use [Flask](https://flask.palletsprojects.com/), a lightweight Python web +framework, +to create your application. + +1. In your `~/iot-api-apps` directory, open a terminal and enter the following commands to create and navigate into a new project directory: + + ```bash + mkdir iot-api-python && cd $_ + ``` + +2. Enter the following commands in your terminal to create and activate a Python virtual environment for the project: + + ```bash + # Create a new virtual environment named "virtualenv" + # Python 3.8+ + python -m venv virtualenv + + # Activate the virtualenv (OS X & Linux) + source virtualenv/bin/activate + ``` + +3. After activation completes, enter the following commands in your terminal to install Flask with the `pip` package installer (included with Python): + + ```bash + pip install Flask + ``` + +4. In your project, create a `app.py` file that: + + 1. Imports the Flask package. + 2. Instantiates a Flask application. + 3. Provides a route to execute the application. + + ```python + from flask import Flask + app = Flask(__name__) + + @app.route("/") + def hello(): + return "Hello World!" + ``` + + {{% caption %}}[influxdata/iot-api-python app.py](https://github.com/influxdata/iot-api-python/blob/main/app.py){{% /caption %}} + + Start your application. + The following example code starts the application + on `http://localhost:3001` with debugging and hot-reloading enabled: + + ```bash + export FLASK_ENV=development + flask run -h localhost -p 3001 + ``` + + In your browser, visit to view the “Hello World!” response. + +## Install InfluxDB client library + +The InfluxDB client library provides the following InfluxDB API interactions: + +- Query data with the Flux language. +- Write data to InfluxDB. +- Batch data in the background. +- Retry requests automatically on failure. + +Enter the following command into your terminal to install the client library: + +```bash +pip install influxdb-client +``` + +For more information about the client library, see the [influxdata/influxdb-client-python repo](https://github.com/influxdata/influxdb-client-python). + +## Configure the client library + +InfluxDB client libraries require configuration properties from your InfluxDB environment. +Typically, you'll provide the following properties as environment variables for your application: + +- `INFLUX_URL` +- `INFLUX_TOKEN` +- `INFLUX_ORG` +- `INFLUX_BUCKET` +- `INFLUX_BUCKET_AUTH` + +To set up the client configuration, create a `config.ini` in your project's top +level directory and paste the following to provide the necessary InfluxDB credentials: + +```ini +[APP] +INFLUX_URL = +INFLUX_TOKEN = +INFLUX_ORG = +INFLUX_BUCKET = iot_center +INFLUX_BUCKET_AUTH = iot_center_devices +``` + +{{% caption %}}[/iot-api-python/config.ini](https://github.com/influxdata/iot-api-python/blob/main/config.ini){{% /caption %}} + +Replace the following: + +- **``**: your InfluxDB instance URL. +- **``**: your InfluxDB [API token](#authorization) with permission to query (_read_) buckets +and create (_write_) authorizations for devices. +- **``**: your InfluxDB organization ID. + +## Build the API + +Your application API provides server-side HTTP endpoints that process requests from the UI. +Each API endpoint is responsible for the following: + +1. Listen for HTTP requests (from the UI). +2. Translate requests into InfluxDB API requests. +3. Process InfluxDB API responses and handle errors. +4. Respond with status and data (for the UI). + +## Create the API to register devices + +In this application, a _registered device_ is a point that contains your device ID, authorization ID, and API token. +The API token and authorization permissions allow the device to query and write to `INFLUX_BUCKET`. +In this section, you add the API endpoint that handles requests from the UI, creates an authorization in InfluxDB, +and writes the registered device to the `INFLUX_BUCKET_AUTH` bucket. +To learn more about API tokens and authorizations, see [Manage API tokens](/influxdb/v2.2/security/tokens/) + +The application API uses the following `/api/v2` InfluxDB API endpoints: + +- `POST /api/v2/query`: to query `INFLUX_BUCKET_AUTH` for a registered device. +- `GET /api/v2/buckets`: to get the bucket ID for `INFLUX_BUCKET`. +- `POST /api/v2/authorizations`: to create an authorization for the device. +- `POST /api/v2/write`: to write the device authorization to `INFLUX_BUCKET_AUTH`. + +### Create an authorization for the device + +In this section, you create an authorization with _read_-_write_ permission to `INFLUX_BUCKET` and receive an API token for the device. +The example below uses the following steps to create the authorization: + +1. Instantiate the `AuthorizationsAPI` client and `BucketsAPI` client with the configuration. +2. Retrieve the bucket ID. +3. Use the client library to send a `POST` request to the `/api/v2/authorizations` InfluxDB API endpoint. + +Create a `./api/devices.py` file that contains the following: + +{{< code-tabs-wrapper >}} +{{% code-tabs %}} +[Python](#python) +{{% /code-tabs %}} +{{% code-tab-content %}} + +{{% truncate %}} + +```python +# Import the dependencies. +import configparser +from datetime import datetime +from uuid import uuid4 + +# Import client library classes. +from influxdb_client import Authorization, InfluxDBClient, Permission, PermissionResource, Point, WriteOptions +from influxdb_client.client.authorizations_api import AuthorizationsApi +from influxdb_client.client.bucket_api import BucketsApi +from influxdb_client.client.query_api import QueryApi +from influxdb_client.client.write_api import SYNCHRONOUS + +from api.sensor import Sensor + +# Get the configuration key-value pairs. + +config = configparser.ConfigParser() +config.read('config.ini') + +def create_authorization(device_id) -> Authorization: + influxdb_client = InfluxDBClient(url=config.get('APP', 'INFLUX_URL'), + token=os.environ.get('INFLUX_TOKEN'), + org=os.environ.get('INFLUX_ORG')) + + authorization_api = AuthorizationsApi(influxdb_client) + # get bucket_id from bucket + buckets_api = BucketsApi(influxdb_client) + buckets = buckets_api.find_bucket_by_name(config.get('APP', 'INFLUX_BUCKET')) # function returns only 1 bucket + bucket_id = buckets.id + org_id = buckets.org_id + desc_prefix = f'IoTCenterDevice: {device_id}' + org_resource = PermissionResource(org_id=org_id, id=bucket_id, type="buckets") + read = Permission(action="read", resource=org_resource) + write = Permission(action="write", resource=org_resource) + permissions = [read, write] + authorization = Authorization(org_id=org_id, permissions=permissions, description=desc_prefix) + request = authorization_api.create_authorization(authorization) + return request +``` + +{{% /truncate %}} +{{% caption %}}[iot-api-python/api/devices.py](https://github.com/influxdata/iot-api-python/blob/d389a0e072c7a03dfea99e5663bdc32be94966bb/api/devices.py#L145){{% /caption %}} + +To create an authorization that has _read_-_write_ permission to `INFLUX_BUCKET`, you need the bucket ID. +To retrieve the bucket ID, `create_authorization(deviceId)` calls the +`BucketsAPI find_bucket_by_name` function that sends a `GET` request to +the `/api/v2/buckets` InfluxDB API endpoint. +`create_authorization(deviceId)` then passes a new authorization in the request body with the following: + +- Bucket ID. +- Organization ID. +- Description: `IoTCenterDevice: DEVICE_ID`. +- List of permissions to the bucket. + +To learn more about API tokens and authorizations, see [Manage API tokens](/influxdb/v2.2/security/tokens/). + +Next, [write the device authorization to a bucket](#write-the-device-authorization-to-a-bucket). + +### Write the device authorization to a bucket + +With a device authorization in InfluxDB, write a point for the device and authorization details to `INFLUX_BUCKET_AUTH`. +Storing the device authorization in a bucket allows you to do the following: + +- Report device authorization history. +- Manage devices with and without tokens. +- Assign the same token to multiple devices. +- Refresh tokens. + +To write a point to InfluxDB, use the InfluxDB client library to send a `POST` request to the `/api/v2/write` InfluxDB API endpoint. +In `./api/devices.py`, add the following `create_device(device_id)` function: + +{{< code-tabs-wrapper >}} +{{% code-tabs %}} +[Python](#python) +{{% /code-tabs %}} +{{% code-tab-content %}} + +```python +def create_device(device_id=None): + influxdb_client = InfluxDBClient(url=config.get('APP', 'INFLUX_URL'), + token=config.get('APP', 'INFLUX_TOKEN'), + org=config.get('APP', 'INFLUX_ORG')) + if device_id is None: + device_id = str(uuid4()) + write_api = influxdb_client.write_api(write_options=SYNCHRONOUS) + point = Point('deviceauth') \ + .tag("deviceId", device_id) \ + .field('key', f'fake_auth_id_{device_id}') \ + .field('token', f'fake_auth_token_{device_id}') + client_response = write_api.write(bucket=config.get('APP', 'INFLUX_BUCKET_AUTH'), record=point) + # write() returns None on success + if client_response is None: + return device_id + # Return None on failure + return None +``` + +{{% caption %}}[iot-api-python/api/devices.py](https://github.com/influxdata/iot-api-python/blob/f354941c80b6bac643ca29efe408fde1deebdc96/api/devices.py#L47){{% /caption %}} + +{{% /code-tab-content %}} +{{< /code-tabs-wrapper >}} + +`create_device(device_id)` takes a _`device_id`_ and writes data to `INFLUX_BUCKET_AUTH` in the following steps: + +1. Initialize `InfluxDBClient()` with `url`, `token`, and `org` values from the configuration. +2. Initialize a `WriteAPI` client for writing data to an InfluxDB bucket. +3. Create a `Point`. +4. Use `write_api.write()` to write the `Point` to the bucket. +5. Check for failures--if the write was successful, `write_api` returns `None`. +6. Return _`device_id`_ if successful; `None` otherwise. + +The function writes a point with the following elements: + +| Element | Name | Value | +|:------------|:-----------|:--------------------------| +| measurement | | `deviceauth` | +| tag | `deviceId` | device ID | +| field | `key` | authorization ID | +| field | `token` | authorization (API) token | + +Next, [create the API to list devices](#create-the-api-to-list-devices). + +## Create the API to list devices + +Add the `/api/devices` API endpoint that retrieves, processes, and lists registered devices. + +1. Create a Flux query that gets the last row of each [series](/influxdb/v2.2/reference/glossary#series) that contains a `deviceauth` measurement. + The example query below returns rows that contain the `key` field (authorization ID) and excludes rows that contain a `token` field (to avoid exposing tokens to the UI). + + ```js + // Flux query finds devices + from(bucket:`${INFLUX_BUCKET_AUTH}`) + |> range(start: 0) + |> filter(fn: (r) => r._measurement == "deviceauth" and r._field != "token") + |> last() + ``` + +2. Use the `QueryApi` client to send the Flux query to the `POST /api/v2/query` InfluxDB API endpoint. + + In `./api/devices.py`, add the following: + + {{< code-tabs-wrapper >}} + {{% code-tabs %}} + [Python](#python) + {{% /code-tabs %}} + {{% code-tab-content %}} + + {{% truncate %}} + + ```python + def get_device(device_id=None) -> {}: + influxdb_client = InfluxDBClient(url=config.get('APP', 'INFLUX_URL'), + token=os.environ.get('INFLUX_TOKEN'), + org=os.environ.get('INFLUX_ORG')) + # Queries must be formatted with single and double quotes correctly + query_api = QueryApi(influxdb_client) + device_filter = '' + if device_id: + device_id = str(device_id) + device_filter = f'r.deviceId == "{device_id}" and r._field != "token"' + else: + device_filter = f'r._field != "token"' + + flux_query = f'from(bucket: "{config.get("APP", "INFLUX_BUCKET_AUTH")}") ' \ + f'|> range(start: 0) ' \ + f'|> filter(fn: (r) => r._measurement == "deviceauth" and {device_filter}) ' \ + f'|> last()' + + response = query_api.query(flux_query) + result = [] + for table in response: + for record in table.records: + try: + 'updatedAt' in record + except KeyError: + record['updatedAt'] = record.get_time() + record[record.get_field()] = record.get_value() + result.append(record.values) + return result + ``` + +{{% /truncate %}} + +{{% caption %}}[iot-api-python/api/devices.py get_device()](https://github.com/influxdata/iot-api-python/blob/9bf44a659424a27eb937d545dc0455754354aef5/api/devices.py#L30){{% /caption %}} + +{{% /code-tab-content %}} +{{< /code-tabs-wrapper >}} + +The `get_device(device_id)` function does the following: + +1. Instantiates a `QueryApi` client and sends the Flux query to InfluxDB. +2. Iterates over the `FluxTable` in the response and returns a list of tuples. + +## Create IoT virtual device + +Create a `./api/sensor.py` file that generates simulated weather telemetry data. +Follow the [example code](https://github.com/influxdata/iot-api-python/blob/f354941c80b6bac643ca29efe408fde1deebdc96/api/sensor.py) to create the IoT virtual device. + +Next, generate data for virtual devices and [write the data to InfluxDB](#write-telemetry-data). + +## Write telemetry data + +In this section, you write telemetry data to an InfluxDB bucket. +To write data, use the InfluxDB client library to send a `POST` request to the `/api/v2/write` InfluxDB API endpoint. + +The example below uses the following steps to generate data and then write it to InfluxDB: + +1. Initialize a `WriteAPI` instance. +2. Create a `Point` with the `environment` measurement and data fields for temperature, humidity, pressure, latitude, and longitude. +3. Use the `WriteAPI write` method to send the point to InfluxDB. + +In `./api/devices.py`, add the following `write_measurements(device_id)` function: + +{{< code-tabs-wrapper >}} +{{% code-tabs %}} +[Python](#python) +{{% /code-tabs %}} +{{% code-tab-content %}} + +```python +def write_measurements(device_id): + influxdb_client = InfluxDBClient(url=config.get('APP', 'INFLUX_URL'), + token=config.get('APP', 'INFLUX_TOKEN'), + org=config.get('APP', 'INFLUX_ORG')) + write_api = influxdb_client.write_api(write_options=SYNCHRONOUS) + virtual_device = Sensor() + coord = virtual_device.geo() + point = Point("environment") \ + .tag("device", device_id) \ + .tag("TemperatureSensor", "virtual_bme280") \ + .tag("HumiditySensor", "virtual_bme280") \ + .tag("PressureSensor", "virtual_bme280") \ + .field("Temperature", virtual_device.generate_measurement()) \ + .field("Humidity", virtual_device.generate_measurement()) \ + .field("Pressure", virtual_device.generate_measurement()) \ + .field("Lat", coord['latitude']) \ + .field("Lon", coord['latitude']) \ + .time(datetime.utcnow()) + print(f"Writing: {point.to_line_protocol()}") + client_response = write_api.write(bucket=config.get('APP', 'INFLUX_BUCKET'), record=point) + # write() returns None on success + if client_response is None: + # TODO Maybe also return the data that was written + return device_id + # Return None on failure + return None +``` + +{{% caption %}}[iot-api-python/api/devices.py write_measurement()](https://github.com/influxdata/iot-api-python/blob/f354941c80b6bac643ca29efe408fde1deebdc96/api/devices.py){{% /caption %}} + +{{% /code-tab-content %}} +{{< /code-tabs-wrapper >}} + +## Query telemetry data + +In this section, you retrieve telemetry data from an InfluxDB bucket. +To retrieve data, use the InfluxDB client library to send a `POST` request to the `/api/v2/query` InfluxDB API endpoint. +The example below uses the following steps to retrieve and process telemetry data: + + 1. Query `environment` measurements in `INFLUX_BUCKET`. + 2. Filter results by `device_id`. + 3. Return CSV data that the [`influxdata/giraffe` UI library](https://github.com/influxdata/giraffe) can process. + +In `./api/devices.py`, add the following `get_measurements(device_id)` function: + +{{< code-tabs-wrapper >}} +{{% code-tabs %}} +[Python](#python) +{{% /code-tabs %}} +{{% code-tab-content %}} + +```python +def get_measurements(query): + influxdb_client = InfluxDBClient(url=config.get('APP', 'INFLUX_URL'), + token=os.environ.get('INFLUX_TOKEN'), org=os.environ.get('INFLUX_ORG')) + query_api = QueryApi(influxdb_client) + result = query_api.query_csv(query, + dialect=Dialect( + header=True, + delimiter=",", + comment_prefix="#", + annotations=['group', 'datatype', 'default'], + date_time_format="RFC3339")) + response = '' + for row in result: + response += (',').join(row) + ('\n') + return response +``` + +{{% caption %}}[iot-api-python/api/devices.py get_measurements()](https://github.com/influxdata/iot-api-python/blob/9bf44a659424a27eb937d545dc0455754354aef5/api/devices.py#L122){{% /caption %}} + +{{% /code-tab-content %}} +{{< /code-tabs-wrapper >}} + +## Define API responses + +In `app.py`, add API endpoints that match incoming requests and respond with the results of your modules. +In the following `/api/devices/` route example, `app.py` retrieves _`device_id`_ from `GET` and `POST` requests, passes it to the `get_device(device_id)` method and returns the result as JSON data with CORS `allow-` headers. + +{{< code-tabs-wrapper >}} +{{% code-tabs %}} +[Python](#python) +{{% /code-tabs %}} +{{% code-tab-content %}} + +```python +@app.route('/api/devices/', methods=['GET', 'POST']) +def api_get_device(device_id): + if request.method == "OPTIONS": # CORS preflight + return _build_cors_preflight_response() + return _corsify_actual_response(jsonify(devices.get_device(device_id))) +``` + +{{% caption %}}[iot-api-python/app.py](https://github.com/influxdata/iot-api-python/blob/9bf44a659424a27eb937d545dc0455754354aef5/app.py){{% /caption %}} + +{{% /code-tab-content %}} +{{< /code-tabs-wrapper >}} + +Enter the following commands into your terminal to restart the application: + + 1. `CONTROL+C` to stop the application. + 2. `flask run -h localhost -p 3001` to start the application. + +To retrieve devices data from your API, visit in your browser. + +## Install and run the UI + +`influxdata/iot-api-ui` is a standalone [Next.js React](https://nextjs.org/docs/basic-features/pages) UI that uses your application API to write and query data in InfluxDB. +`iot-api-ui` uses Next.js _[rewrites](https://nextjs.org/docs/api-reference/next.config.js/rewrites)_ to route all requests in the `/api/` path to your API. + +To install and run the UI, do the following: + +1. In your `~/iot-api-apps` directory, clone the [`influxdata/iot-api-ui` repo](https://github.com/influxdata/iot-api-ui) and go into the `iot-api-ui` directory--for example: + + ```bash + cd ~/iot-api-apps + git clone git@github.com:influxdata/iot-api-ui.git + cd ./iot-app-ui + ``` + +2. The `./.env.development` file contains default configuration settings that you can + edit or override (with a `./.env.local` file). +3. To start the UI, enter the following command into your terminal: + + ```bash + yarn dev + ``` + + To view the list and register devices, visit in your browser. + +To learn more about the UI components, see [`influxdata/iot-api-ui`](https://github.com/influxdata/iot-api-ui). diff --git a/layouts/shortcodes/api/iot-starter-intro b/layouts/shortcodes/api/iot-starter-intro new file mode 100644 index 000000000..da7c49ccf --- /dev/null +++ b/layouts/shortcodes/api/iot-starter-intro @@ -0,0 +1,10 @@ +Follow this step-by-step tutorial to build an Internet-of-Things (IoT) application with InfluxData client libraries and your favorite framework or language. + +In this tutorial, you'll use the InfluxDB API and +client libraries to build a modern application as you learn the following: + +- InfluxDB core concepts. +- How the application interacts with devices and InfluxDB. +- How to authenticate apps and devices to the API. +- How to install a client library. +- How to write and query data in InfluxDB. diff --git a/shared/text/api/v2.0/client-library-examples/nodejs/server/devices/_devices.js b/shared/text/api/v2.0/client-library-examples/nodejs/server/devices/_devices.js new file mode 100644 index 000000000..141fd5702 --- /dev/null +++ b/shared/text/api/v2.0/client-library-examples/nodejs/server/devices/_devices.js @@ -0,0 +1,49 @@ +import { flux } from '@influxdata/influxdb-client' +import influxdb from '../_influxdb' + +const INFLUX_ORG = process.env.INFLUX_ORG +const INFLUX_BUCKET_AUTH = process.env.INFLUX_BUCKET_AUTH + +/** + * Gets devices or a particular device when deviceId is specified. Tokens + * are not returned unless deviceId is specified. It can also return devices + * with empty/unknown key, such devices can be ignored (InfluxDB authorization is not associated). + * @param deviceId optional deviceId + * @returns promise with an Record. + */ + export async function getDevices(deviceId) { + const queryApi = influxdb.getQueryApi(INFLUX_ORG) + const deviceFilter = + deviceId !== undefined + ? flux` and r.deviceId == "${deviceId}"` + : flux` and r._field != "token"` + const fluxQuery = flux`from(bucket:${INFLUX_BUCKET_AUTH}) + |> range(start: 0) + |> filter(fn: (r) => r._measurement == "deviceauth"${deviceFilter}) + |> last()` + const devices = {} + console.log(`*** QUERY *** \n ${fluxQuery}`) + return await new Promise((resolve, reject) => { + queryApi.queryRows(fluxQuery, { + next(row, tableMeta) { + const o = tableMeta.toObject(row) + const deviceId = o.deviceId + if (!deviceId) { + return + } + const device = devices[deviceId] || (devices[deviceId] = {deviceId}) + device[o._field] = o._value + if (!device.updatedAt || device.updatedAt < o._time) { + device.updatedAt = o._time + } + }, + error: reject, + complete() { + console.log(JSON.stringify(devices)) + resolve(devices) + }, + }) + }) + } + + \ No newline at end of file diff --git a/shared/text/api/v2.0/client-library-examples/nodejs/server/devices/create.js b/shared/text/api/v2.0/client-library-examples/nodejs/server/devices/create.js new file mode 100644 index 000000000..ece1c404d --- /dev/null +++ b/shared/text/api/v2.0/client-library-examples/nodejs/server/devices/create.js @@ -0,0 +1,78 @@ +import { getDevices } from './devices/_devices' + +import influxdb from '../_influxdb' +import { AuthorizationsAPI, BucketsAPI } from '@influxdata/influxdb-client-apis' +import { Point } from '@influxdata/influxdb-client' + +const INFLUX_ORG = process.env.INFLUX_ORG +const INFLUX_BUCKET_AUTH = process.env.INFLUX_BUCKET_AUTH +const INFLUX_BUCKET = process.env.INFLUX_BUCKET + +export default async function handler(req, res) { + try { + const deviceId = JSON.parse(req.body)?.deviceId + const devices = await createDevice(deviceId) + res.status(200).json( + Object.values(devices) + .filter((x) => x.deviceId && x.key) // ignore deleted or unknown devices + .sort((a, b) => a.deviceId.localeCompare(b.deviceId)) + ) + } catch(err) { + res.status(500).json({ error: `failed to load data: ${err}` }) + } +} + +/** Creates an authorization for a deviceId and writes it to a bucket */ +async function createDevice(deviceId) { + let device = (await getDevices(deviceId)) || {} + let authorizationValid = !!Object.values(device)[0]?.key + if(authorizationValid) { + console.log(JSON.stringify(device)) + return Promise.reject('This device ID is already registered and has an authorization.') + } else { + console.log(`createDeviceAuthorization: deviceId=${deviceId}`) + const authorization = await createAuthorization(deviceId) + const writeApi = influxdb.getWriteApi(INFLUX_ORG, INFLUX_BUCKET_AUTH, 'ms', { + batchSize: 2, + }) + const point = new Point('deviceauth') + .tag('deviceId', deviceId) + .stringField('key', authorization.id) + .stringField('token', authorization.token) + writeApi.writePoint(point) + await writeApi.close() + return authorization + } +} + + /** + * Creates an authorization for a supplied deviceId + * @param {string} deviceId client identifier + * @returns {import('@influxdata/influxdb-client-apis').Authorization} promise with authorization or an error + */ + async function createAuthorization(deviceId) { + const authorizationsAPI = new AuthorizationsAPI(influxdb) + const bucketsAPI = new BucketsAPI(influxdb) + const DESC_PREFIX = 'IoTCenterDevice: ' + + const buckets = await bucketsAPI.getBuckets({name: INFLUX_BUCKET, orgID: INFLUX_ORG}) + const bucketId = buckets.buckets[0]?.id + + return await authorizationsAPI.postAuthorizations({ + body: { + orgID: INFLUX_ORG, + description: DESC_PREFIX + deviceId, + permissions: [ + { + action: 'read', + resource: {type: 'buckets', id: bucketId, orgID: INFLUX_ORG}, + }, + { + action: 'write', + resource: {type: 'buckets', id: bucketId, orgID: INFLUX_ORG}, + }, + ], + }, + }) + } + \ No newline at end of file