import Ajv2019 from 'ajv/dist/2019'; import * as commonSchema from './schemas/common-schema.json'; import * as deviceAgentStatusSchema from './schemas/device-agent-status-schema.json'; import * as envVarSchema from './schemas/env-var-schema.json'; import * as secureTunnelSchema from './schemas/secure-tunnel-schema.json'; import * as shadowProjectUpdateLegacySchema from './schemas/shadow-project-update-legacy-schema.json'; import * as shadowProjectUpdateSchema from './schemas/shadow-project-update-schema.json'; import * as shadowProjectsUpdateAllSchema from './schemas/shadow-projects-update-all-schema.json'; import * as systemInformationSchema from './schemas/system-information-schema.json'; import { keyMirror } from './utils'; const ajv = new Ajv2019({ allErrors: true, schemas: [commonSchema] }); export function getDesiredFromMessage(message) { if (message?.state?.desired) { return message.state.desired; } else return null; } export function getReportedFromMessage(message) { if (message?.state?.reported) { return message.state.reported; } else return null; } export function getUpdateDeltaStateFromMessage(message) { if (message?.state) { return message.state; } else return null; } /* ============================================================================ Shadow Topics ============================================================================ */ const SHADOW_PREFIX = `$aws/things/`; export const ShadowTopics = keyMirror({ get: null, 'get/accepted': null, 'get/rejected': null, update: null, 'update/delta': null, 'update/accepted': null, 'update/rejected': null, delete: null, 'delete/accepted': null, 'delete/rejected': null }); export type ShadowTopics = keyof typeof ShadowTopics; export const SHADOW_TOPICS = Object.keys(ShadowTopics) as ShadowTopics[]; const ShadowNames = keyMirror({ projects: null, 'system-info': null, 'secure-tunnel': null, 'device-agent-status': null }); export type ShadowNames = keyof typeof ShadowNames; export const SHADOW_NAMES = Object.keys(ShadowNames) as ShadowNames[]; export const getShadowTopic = ( clientId: string, shadowName: ShadowNames, shadowTopic: ShadowTopics ) => `${SHADOW_PREFIX}${clientId}/shadow/name/${shadowName}/${shadowTopic}`; /* ============================================================================ Project Shadow ============================================================================ */ export type ShadowUpdateTypes = | ShadowProjectsUpdateAll | SystemInformationShadowUpdate | SecureTunnelShadowUpdate | DeviceAgentStatusShadowUpdate; export interface ProjectShadowUpdate { appConfig?: string; envVars?: string; } export interface ProjectShadowUpdateLegacy { appConfig?: string; envVars?: { [service: string]: { [key: string]: string | null; }; }; } export interface ShadowProjectsUpdateAll { [projectId: string]: ProjectShadowUpdate | null; } // Shadow Message validator functions ajv.addSchema(shadowProjectUpdateSchema, 'shadow-project-update-schema.json'); export const validateProjectShadowUpdate = ajv.compile( shadowProjectUpdateSchema ); ajv.addSchema( shadowProjectUpdateLegacySchema, 'shadow-project-update-legacy-schema.json' ); export const validateLegacyProjectShadowUpdate = ajv.compile(shadowProjectUpdateSchema); ajv.addSchema( shadowProjectsUpdateAllSchema, 'shadow-projects-update-all-schema.json' ); export const validateShadowProjectsUpdateAll = ajv.compile(shadowProjectsUpdateAllSchema); /* ============================================================================ System Information Shadow ============================================================================ */ export interface SystemInformationShadowUpdate { os?: { platform?: string; distro?: string; release?: string; kernel?: string; architecture?: string; hostname?: string; }; cpu?: { manufacturer?: string; brand?: string; vendor?: string; model?: string; cores?: string; physicalCores?: string; efficiencyCores?: string; processors?: string; }; disk?: { drives: { device?: string; type?: string; name?: string; vendor?: string; size?: string; }[]; }; device?: { manufacturer?: string; model?: string; version?: string; serial?: string; virtual?: boolean; // boolean on purpose }; network?: { ipv4Address?: string; ipv6Address?: string; macAddress?: string; }; versions?: { agent?: string; deviceAgentSchemas?: string; npm?: string; node?: string; docker?: string; dockerCompose?: string; }; lastBootTime?: string; } ajv.addSchema(systemInformationSchema, 'system-information-schema.json'); export const validateSystemInformationShadowUpdate = ajv.compile(systemInformationSchema); /* ============================================================================ Environment Variables Shadow ============================================================================ */ export interface EnvVars { [service: string]: { [name: string]: string | null; }; } ajv.addSchema(envVarSchema, 'env-var-schema.json'); export const validateEnvVarSchemaShadowUpdate = ajv.compile(envVarSchema); /* ============================================================================ Secure Tunnel Shadow ============================================================================ */ export type SecureTunnelPortInfo = { enabled: boolean; type: string; ip: string; port: number; }; export type SecureTunnelShadowUpdate = { st_ports: SecureTunnelPortInfo[]; }; ajv.addSchema(secureTunnelSchema, 'secure-tunnel-schema.json'); export const validateSecureTunnelShadowUpdate = ajv.compile(secureTunnelSchema); /* ============================================================================ Device Agent Status ============================================================================ */ export const passthroughStatusEnum = keyMirror({ starting: null, running: null, error: null, disabled: null }); export type PassthroughStatusValue = keyof typeof passthroughStatusEnum; export interface DeviceAgentStatusShadowUpdate { passthrough: { status: PassthroughStatusValue; message: string; }; } ajv.addSchema(deviceAgentStatusSchema, 'device-agent-status-schema.json'); export const validateDeviceAgentStatusShadowUpdate = ajv.compile(deviceAgentStatusSchema); /* ============================================================================ Shadow Message Builders ============================================================================ */ export function buildUpdateShadowMessage(opts: { clientId: string; reported?: T; desired?: T; }) { const { clientId, reported, desired } = opts; const state = { reported, desired }; const message = { ...buildBaseShadowMessage(clientId), state }; return message; } export function buildBaseShadowMessage(clientId: string) { const baseShadowMessage = { clientToken: clientId }; return baseShadowMessage; }