UNPKG

@dodi-smart/ttlock-graphql-api

Version:
373 lines (280 loc) • 10.7 kB
<p align="center"> <img alt="ttlock" src="https://is4-ssl.mzstatic.com/image/thumb/Purple116/v4/39/5c/de/395cde8e-fdce-5955-d73e-2b277723f62e/AppIcon-0-1x_U007emarketing-0-7-0-0-sRGB-85-220.png/230x0w.webp" height="128px"> </p> <h1 align="center">TTLock GraphQL API</h1> <h2 align="center">@dodi-smart/ttlock-graphql-api</h2> <p align="center"> <img alt="npm" src="https://img.shields.io/npm/v/@dodi-smart/ttlock-graphql-api"> <img alt="npm" src="https://img.shields.io/npm/dm/@dodi-smart/ttlock-graphql-api"> <img alt="NPM" src="https://img.shields.io/npm/l/@dodi-smart/ttlock-graphql-api"> </p> This package creates a TTLock GraphQL API, allowing for interaction with data at TTLock. **It is unofficial TTLock GraphQL API generated based on documentation, mainly configured to be used as Hasura Remote Schema.** Here's an example of how to use the TTLock GraphQL API to get a list of smart locks of the configured TTLock user: ```graphql query MyLocks { ttlock { smartlocks { lockName lockId details { state { state } electricQuantity firmwareRevision hardwareRevision } } } } ``` It's recommended to add the TTLock GraphQL API as a [Remote Schema](https://hasura.io/docs/latest/remote-schemas/index/) in Hasura and connect data from your database with data in TTLock. By doing so, it's possible to request data from your database and TTLock in a single GraphQL query. Here's an example of how to use the TTLock GraphQL API to get a list of smart locks for a specific TTLock user. Note that the user data is fetched from your database and the TTLock user data is fetched trough the TTLock API: ```graphql query UsersLocks { users { id displayName ttlocks { # Remote schema relation in users table lockName lockId details { state { state } electricQuantity firmwareRevision hardwareRevision } } } } ``` ## Supported APIs APIs can be added upon request. PRs are welcome of course! https://euopen.ttlock.com/document/doc?urlName=userGuide%2FekeyEn.html | Api | Supported | Comment | | ----------------- | :-------: | ------- | | User | šŸ”“ | | | Lock | 🟠 | | | Ekey | šŸ”“ | | | Passcode | 🟢 | | | Gateway | 🟠 | | | AddressToken | šŸ”“ | | | IC Card | šŸ”“ | | | Fingerprint | šŸ”“ | | | Group | šŸ”“ | | | Unlock record | 🟠 | | | Lock upgrade | šŸ”“ | | | Wireless Keyboard | 🟢 | | | Remote | šŸ”“ | | | Door Sensor | šŸ”“ | | | NB-IoT | šŸ”“ | | | QR Code | šŸ”“ | | | WiFi Lock | šŸ”“ | | 🟢 - Supported 🟠 - Partial šŸ”“ - Not supported ## Getting Started Install the package: ``` pnpm add @dodi-smart/ttlock-graphql-api ``` ### Setup serverless function Create a new [Serverless Function](https://docs.nhost.io/serverless-functions): `functions/graphql/ttlock.ts`: ```ts import { createTTlockGraphQLServer } from '@dodi-smart/ttlock-graphql-api' const server = createTTlockGraphQLServer({ graphiql: true, graphqlEndpoint: '/graphql/ttlock', healthCheckEndpoint: '/graphql/ttlock/health', provideAuth(context) { return { // Should provide authentication } }, onUpdateSession(context, auth) { // Handle session update } }) export default server ``` ### Setup TTLock Management Go to [TTLock Management](https://euopen.ttlock.com/) and obtain OAuth2 API key and secret by creating an application and wait for approval. - Add `TTLOCK_CLIENT_ID` as an environment variable. - Add `TTLOCK_CLIENT_SECRET` as an environment variable. If you're using Nhost, add `TTLOCK_CLIENT_ID`, `TTLOCK_CLIENT_SECRET` to `.env.development` like this: ```ini TTLOCK_CLIENT_ID=6EYY•••• TTLOCK_CLIENT_SECRET=BPum•••• ``` And add the production key values to environment variables in the Nhost dashboard. ### Start Nhost ```sh nhost up ``` Learn more about the [Nhost CLI](https://docs.nhost.io/cli). ### Test Test the TTLock GraphQL API in the browser: https://local.functions.nhost.run/v1/graphql/ttlock ### Remote Schema Add the TTLock GraphQL API as a Remote Schema in Hasura. **URL** ``` {{NHOST_FUNCTIONS_URL}}/graphql/ttlock ``` **Headers** ``` x-nhost-webhook-secret: NHOST_WEBHOOK_SECRET (From env var) ``` > The `NHOST_WEBHOOK_SECRET` is used to verify that the request is coming from Nhost. The environment variable is a [system environment variable](/platform/environment-variables#system-environment-variables) and is always available. ## Authentication You have several options to authentication your users agains TTLock using this library. All of them depends on the usecase: **Single user usecase (easy to setup)** When the system should work with the locks of a single user (administrator). Permissions and access to the locks are granted to others users via the administrator or using Hasura user roles. **Multi user usecase (advanced)** When system work with locks of multiple users. Each user can authenticate against TTLock using their own credentials via OAuth 2.0. This usecase can be used for multi tenant apps for example. ### How to choose | Use case | Using JWT Token (easy) | OAuth 2.0 (advanced) | | ----------- | :--------------------: | :------------------: | | Single user | 🟠 | 🟢 | | Multi user | šŸ”“ | 🟢 | 🟢 - Preferable way 🟠 - Supported šŸ”“ - Not supported ### Using JWT Token Obtain a JWT token by logging the user via `curl`, `paw` or `postman`. The Refresh token is valid for 10 years. - Add `TTLOCK_ACCESS_TOKEN` as an environment variable. - Add `TTLOCK_REFRESH_TOKEN` as an environment variable. If you're using Nhost, add `TTLOCK_ACCESS_TOKEN` and `TTLOCK_REFRESH_TOKEN` to `.env.development` like this: ```ini TTLOCK_ACCESS_TOKEN=59f6d•••• TTLOCK_REFRESH_TOKEN=BPum•••• ``` And add the production key values to environment variables in the Nhost dashboard. #### Configure the TTLock GraphQL API ```ts import { createTTLockGraphQLServer } from '@dodi-smart/ttlock-graphql-api' const server = createTTLockGraphQLServer({ graphqlEndpoint: '/graphql/ttlock', healthCheckEndpoint: '/graphql/ttlock/health', provideAuth(context) { const { request } = context return { accessToken: process.env["TTLOCK_ACCESS_TOKEN"] refreshToken: process.env["TTLOCK_REFRESH_TOKEN"] } }, onUpdateSession(context, auth) { // Can be empty as TTLOCK_REFRESH_TOKEN is valid for 10 years } }) export default server ``` ### OAuth 2.0 From the TTLock Management page get client id and secret. - Add `TTLOCK_CLIENT_ID` as an environment variable. - Add `TTLOCK_CLIENT_SECRET` as an environment variable. If you're using Nhost, add `TTLOCK_CLIENT_ID` and `TTLOCK_CLIENT_SECRET` to `.env.development` like this: ```ini TTLOCK_CLIENT_ID=59f6d•••• TTLOCK_CLIENT_SECRET=BPum•••• ``` #### Configure the TTLock GraphQL API ```ts import { createTTLockGraphQLServer } from '@dodi-smart/ttlock-graphql-api' const server = createTTLockGraphQLServer({ graphqlEndpoint: '/graphql/ttlock', healthCheckEndpoint: '/graphql/ttlock/health', provideAuth(context) { const { userClaims } = context const { user } = await gqlSDK.getUser({ id: userId }) return { // Example if ttlock creds are saved in user's metadata, can differ accessToken: user.metadata.ttlockAccessToken, refreshToken: user.metadata.ttlockRefreshToken, } }, onUpdateSession(context, auth) { const { userClaims } = context const { accessToken, refreshToken } = auth await gqlSDK.updateUserTTLockAuth({ id: userId, accessToken, refreshToken }) } }) export default server ``` ## Permissions Here's a minimal example without any custom permissions. Only requests using the `x-hasura-admin-secret` header will work: ```ts import { createTTLockGraphQLServer } from '@dodi-smart/ttlock-graphql-api' const server = createTTLockGraphQLServer({ graphqlEndpoint: '/graphql/ttlock', healthCheckEndpoint: '/graphql/ttlock/health', provideAuth(context) { /* ... */ }, onUpdateSession(context, auth) { /* ... */ } }) export default server ``` For more granular permissions, you can pass an `isAllowed` function to the `createTTLockGraphQLServer`. The `isAllowed` function takes a [`context`](#context) as parameter and runs every time the GraphQL server makes a request to TTLock to get or modify data for a specific TTLock user. Here is an example of an `isAllowed` function: ```ts import { createTTLockGraphQLServer } from '@dodi-smart/ttlock-graphql-api'; const isAllowed = (context: Context) => { const { isAdmin, userClaims } = context // allow all requests if they have a valid `x-hasura-admin-secret` if (isAdmin) { return true } // get user id const userId = userClaims['x-hasura-user-id'] // check if the user is signed in if (!userId) { return false } // get more user information from the database const { user } = await gqlSDK.getUser({ id: userId }) if (!user) { return false } // other checks } const server = createTTLockGraphQLServer({ graphqlEndpoint: '/graphql/ttlock', healthCheckEndpoint: '/graphql/ttlock/health', isAllowed, provideAuth(context) { /* ... */ }, onUpdateSession(context, auth) { /* ... */ } }); export default server; ``` ### Context The `context` object contains: - `userClaims` - verified JWT claims from the user's access token. - `isAdmin` - `true` if the request was made using a valid `x-hasura-admin-secret` header. - `request` - [Fetch API Request object](https://developer.mozilla.org/en-US/docs/Web/API/Request) that represents the incoming HTTP request in platform-independent way. It can be useful for accessing headers to authenticate a user - `query` - the DocumentNode that was parsed from the GraphQL query string - `operationName` - the operation name selected from the incoming query - `variables` - the variables that were defined in the query - `extensions` - the extensions that were received from the client Read more about the [default context from GraphQL Yoga](https://www.the-guild.dev/graphql/yoga-server/docs/features/context#default-context).