From 7620980ce3a8ff49b21536d673637c31b7991638 Mon Sep 17 00:00:00 2001 From: Nicole Jung <31713368+purplenicole730@users.noreply.github.com> Date: Thu, 28 Mar 2024 11:42:17 -0700 Subject: [PATCH] RSDK-6838: add provisioning wrappers (#266) --- Makefile | 2 +- src/provisioning/client.test.ts | 67 ++++++++++++++++++++++++++++ src/provisioning/client.ts | 76 ++++++++++++++++++++++++++++++++ src/provisioning/provisioning.ts | 31 +++++++++++++ src/provisioning/types.ts | 15 +++++++ 5 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 src/provisioning/client.test.ts create mode 100644 src/provisioning/client.ts create mode 100644 src/provisioning/provisioning.ts create mode 100644 src/provisioning/types.ts diff --git a/Makefile b/Makefile index 4990ad9af..857a9c998 100644 --- a/Makefile +++ b/Makefile @@ -55,7 +55,7 @@ update-buf: $(node_modules) .PHONY: build-buf build-buf: $(node_modules) clean-buf $(buf) generate $$(./scripts/get-buf-lock-version.js buf.build/googleapis/googleapis) - $(buf) generate $$(./scripts/get-buf-lock-version.js buf.build/viamrobotics/api) --path common,component,robot,service,app + $(buf) generate $$(./scripts/get-buf-lock-version.js buf.build/viamrobotics/api) --path common,component,robot,service,app,provisioning $(buf) generate $$(./scripts/get-buf-lock-version.js buf.build/erdaniels/gostream) $(buf) generate $$(./scripts/get-buf-lock-version.js buf.build/viamrobotics/goutils) diff --git a/src/provisioning/client.test.ts b/src/provisioning/client.test.ts new file mode 100644 index 000000000..b8c8a7faf --- /dev/null +++ b/src/provisioning/client.test.ts @@ -0,0 +1,67 @@ +// @vitest-environment happy-dom + +import { beforeEach, expect, it, vi } from 'vitest'; +import { ProvisioningServiceClient } from '../gen/provisioning/v1/provisioning_pb_service'; +import { RobotClient } from '../robot'; +import { ProvisioningClient } from './client'; + +let provisioning: ProvisioningClient; + +const testProvisioningInfo = { + fragmentId: 'id', + model: 'model', + manufacturer: 'manufacturer', +}; +const testNetworkInfo = { + type: 'type', + ssid: 'ssid', + security: 'security', + signal: 999, + connected: 'true', + lastError: 'last error', +}; +const testSmartMachineStatus = { + provisioningInfo: testProvisioningInfo, + hasSmartMachineCredentials: true, + isOnline: true, + latestConnectionAttempt: testNetworkInfo, + errorsList: ['error', 'err'], +}; + +beforeEach(() => { + RobotClient.prototype.createServiceClient = vi + .fn() + .mockImplementation( + () => new ProvisioningServiceClient('test-provisioning') + ); + + ProvisioningServiceClient.prototype.getSmartMachineStatus = vi + .fn() + .mockImplementation((_req, _md, cb) => { + cb(null, { + toObject: () => testSmartMachineStatus, + }); + }); + + ProvisioningServiceClient.prototype.getNetworkList = vi + .fn() + .mockImplementation((_req, _md, cb) => { + cb(null, { + toObject: () => ({ networksList: [testNetworkInfo] }), + }); + }); + + provisioning = new ProvisioningClient(new RobotClient('host')); +}); + +it('getSmartMachineStatus', async () => { + await expect(provisioning.getSmartMachineStatus()).resolves.toStrictEqual( + testSmartMachineStatus + ); +}); + +it('getNetworkList', async () => { + await expect(provisioning.getNetworkList()).resolves.toStrictEqual([ + testNetworkInfo, + ]); +}); diff --git a/src/provisioning/client.ts b/src/provisioning/client.ts new file mode 100644 index 000000000..fae683c37 --- /dev/null +++ b/src/provisioning/client.ts @@ -0,0 +1,76 @@ +import { type Options } from '../types'; +import pb from '../gen/provisioning/v1/provisioning_pb'; +import { ProvisioningServiceClient } from '../gen/provisioning/v1/provisioning_pb_service'; +import { RobotClient } from '../robot'; +import type { Provisioning } from './provisioning'; +import { promisify } from '../utils'; +import { type CloudConfig, encodeCloudConfig } from './types'; + +export class ProvisioningClient implements Provisioning { + private client: ProvisioningServiceClient; + private readonly options: Options; + + constructor(client: RobotClient, options: Options = {}) { + this.client = client.createServiceClient(ProvisioningServiceClient); + this.options = options; + } + + private get service() { + return this.client; + } + + async getSmartMachineStatus() { + const { service } = this; + const request = new pb.GetSmartMachineStatusRequest(); + this.options.requestLogger?.(request); + + const response = await promisify< + pb.GetSmartMachineStatusRequest, + pb.GetSmartMachineStatusResponse + >(service.getSmartMachineStatus.bind(service), request); + return response.toObject(); + } + + async setNetworkCredentials(type: string, ssid: string, psk: string) { + const { service } = this; + const request = new pb.SetNetworkCredentialsRequest(); + request.setType(type); + request.setSsid(ssid); + request.setPsk(psk); + + this.options.requestLogger?.(request); + + await promisify< + pb.SetNetworkCredentialsRequest, + pb.SetNetworkCredentialsResponse + >(service.setNetworkCredentials.bind(service), request); + } + + async setSmartMachineCredentials(cloud?: CloudConfig) { + const { service } = this; + const request = new pb.SetSmartMachineCredentialsRequest(); + if (cloud) { + request.setCloud(encodeCloudConfig(cloud)); + } + + this.options.requestLogger?.(request); + + await promisify< + pb.SetSmartMachineCredentialsRequest, + pb.SetSmartMachineCredentialsResponse + >(service.setSmartMachineCredentials.bind(service), request); + } + + async getNetworkList() { + const { service } = this; + const request = new pb.GetNetworkListRequest(); + + this.options.requestLogger?.(request); + + const response = await promisify< + pb.GetNetworkListRequest, + pb.GetNetworkListResponse + >(service.getNetworkList.bind(service), request); + return response.toObject().networksList; + } +} diff --git a/src/provisioning/provisioning.ts b/src/provisioning/provisioning.ts new file mode 100644 index 000000000..c43f8c96e --- /dev/null +++ b/src/provisioning/provisioning.ts @@ -0,0 +1,31 @@ +import type { CloudConfig, NetworkInfo, SmartMachineStatus } from './types'; + +export interface Provisioning { + /** Get the status of the Smart Machine */ + getSmartMachineStatus: () => Promise; + + /** + * Set the network credentials of the Smart Machine, so it can connect to the + * internet. + * + * @param type - The type of network. + * @param ssid - The SSID of the network. + * @param psk - The network's passkey. + */ + setNetworkCredentials: ( + type: string, + ssid: string, + psk: string + ) => Promise; + + /** + * Set the Viam credentials of the smart machine credentials, so it connect to + * the Cloud. + * + * @param cloud - The configuration of the Cloud. + */ + setSmartMachineCredentials: (cloud?: CloudConfig) => Promise; + + /** Get the networks that are visible to the Smart Machine. */ + getNetworkList: () => Promise; +} diff --git a/src/provisioning/types.ts b/src/provisioning/types.ts new file mode 100644 index 000000000..b04fd266a --- /dev/null +++ b/src/provisioning/types.ts @@ -0,0 +1,15 @@ +import pb from '../gen/provisioning/v1/provisioning_pb'; + +export type SmartMachineStatus = pb.GetSmartMachineStatusResponse.AsObject; +export type NetworkInfo = pb.NetworkInfo.AsObject; +export type CloudConfig = pb.CloudConfig.AsObject; + +export const encodeCloudConfig = ( + obj: pb.CloudConfig.AsObject +): pb.CloudConfig => { + const result = new pb.CloudConfig(); + result.setId(obj.id); + result.setSecret(obj.secret); + result.setAppAddress(obj.appAddress); + return result; +};