Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

testing with source #155

Merged
merged 1 commit into from
May 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cloud/gcp/functions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ resource "google_storage_bucket" "function-bucket" {
# Generates an archive of the source code compressed as a .zip file.
data "archive_file" "source" {
type = "zip"
source_dir = "../../udmif/event-handler/src"
output_path = "../../udmif/event-handler/dist.zip"
source_dir = "../../udmif/event-handler/dist"
output_path = "../../udmif/event-handler/index.zip"
}
# Add the zipped file to the bucket.
resource "google_storage_bucket_object" "function-object" {
Expand Down
2 changes: 1 addition & 1 deletion udmif/event-handler/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ setupEnv.sh
# mongo output
.mongodb

dist/
#dist/
Binary file removed udmif/event-handler/dist.zip
Binary file not shown.
22 changes: 22 additions & 0 deletions udmif/event-handler/dist/DeviceDao.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Collection } from 'mongodb';
import { DeviceKey, DeviceDocument } from './model';

const options = { upsert: true };

export interface DeviceDao {
upsert(filterQuery: any, updateQuery: any);
}

export class DefaultDeviceDao implements DeviceDao {
constructor(private collection: Collection) {}

async upsert(deviceKey: DeviceKey, deviceDocument: DeviceDocument): Promise<void> {
// we're using upsert which will allow document updates if it already exists and a document cretion if it does not
console.debug('Attempting to write the following device document: ' + JSON.stringify(deviceDocument));

const result = await this.collection.updateOne(deviceKey, { $set: deviceDocument }, options);

// let's log the result to give us some feedback on what occurred during the upsert
console.log(JSON.stringify(result));
}
}
29 changes: 29 additions & 0 deletions udmif/event-handler/dist/DeviceDaoFactory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { DeviceDao, DefaultDeviceDao } from './DeviceDAO';
import { Collection, MongoClient } from 'mongodb';

const COLLECTION_NAME: string = 'device';

export async function getDeviceDAO(): Promise<DeviceDao> {
return new DefaultDeviceDao(await getMongoCollection());
}

async function getMongoCollection(): Promise<Collection> {
const client = await MongoClient.connect(getUri(), {});

// get the collection
const db = process.env.MONGO_DATABASE;
console.log(`Getting the Mongo Collection for db: ${db} collection: ${COLLECTION_NAME}`);
return client.db(db).collection(COLLECTION_NAME);
}

export function getUri(): string {
const protocol = process.env.MONGO_PROTOCOL;
const host = process.env.MONGO_HOST;
const userName = process.env.MONGO_USER;
const password = process.env.MONGO_PWD;

// get the uri
const credentials = userName ? `${userName}:${password}@` : '';
console.log(`Building a new Mongo Client uri: ${protocol}://*****:*****@${host}`);
return `${protocol}://${credentials}${host}`;
}
65 changes: 65 additions & 0 deletions udmif/event-handler/dist/DeviceDocumentFactory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { DeviceDocument, UdmiMessage } from './model';

const POINTSET_SUB_FOLDER = 'pointset';
const SYSTEM_SUB_FOLDER = 'system';
const MODEL = 'model';
const STATE = 'state';

export function createDeviceDocument(message: UdmiMessage): DeviceDocument {
if (isSystemState(message)) {
return createSystemStateDocument(message);
} else if (isSystemModel(message)) {
return createSystemModelDocument(message);
} else {
return createDefaultDeviceDocument(message);
}
}

function createSystemModelDocument(message: UdmiMessage): DeviceDocument {
const deviceDocument: DeviceDocument = createDefaultDeviceDocument(message);

deviceDocument.section = message.data.location.section;
deviceDocument.site = message.data.location.site;

return deviceDocument;
}

function createSystemStateDocument(message: UdmiMessage): DeviceDocument {
const deviceDocument: DeviceDocument = createDefaultDeviceDocument(message);

deviceDocument.make = message.data.hardware.make;
deviceDocument.model = message.data.hardware.model;
deviceDocument.operational = message.data.operational;
deviceDocument.serialNumber = message.data.serial_no;
deviceDocument.firmware = message.data.software.firmware;

return deviceDocument;
}

function createDefaultDeviceDocument(message: UdmiMessage): DeviceDocument {
const deviceDocument: DeviceDocument = {
name: message.attributes.deviceId,
id: message.attributes.deviceNumId,
};

if (message.data.timestamp) {
deviceDocument.lastPayload = message.data.timestamp;
}
return deviceDocument;
}

export function isSystemState(message): boolean {
return isSubFolder(message, SYSTEM_SUB_FOLDER) && isSubType(message, STATE);
}

export function isSystemModel(message): boolean {
return isSubFolder(message, SYSTEM_SUB_FOLDER) && isSubType(message, MODEL);
}

export function isSubFolder(message, folderName: string): boolean {
return message.attributes.subFolder === folderName;
}

export function isSubType(message, type: string): boolean {
return message.attributes.subType === type;
}
37 changes: 37 additions & 0 deletions udmif/event-handler/dist/UdmiMessageHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { DeviceDao } from './DeviceDao';
import { createDeviceDocument } from './DeviceDocumentFactory';
import { DeviceDocument, DeviceKey, UdmiMessage } from './model';

export default class UdmiMessageHandler {
constructor(private deviceDao: DeviceDao) {}

handleUdmiEvent(event: any) {
try {
const message: UdmiMessage = decodeEventData(event);
const deviceKey: DeviceKey = getDeviceKey(message);
const document: DeviceDocument = createDeviceDocument(message);
this.writeDocument(deviceKey, document);
} catch (e) {
console.error('An unexpected error occurred: ', e);
}
}

private async writeDocument(key: DeviceKey, document: DeviceDocument) {
await this.deviceDao.upsert(key, document);
}
}

export function decodeEventData(event: any): UdmiMessage {
const stringData = Buffer.from(event.data, 'base64').toString();
event.data = JSON.parse(stringData);
console.debug('Decoded event: ', JSON.stringify(event));
return event;
}

export function getDeviceKey(message: UdmiMessage): DeviceKey {
if (!message.attributes.deviceId || !message.attributes.deviceNumId) {
throw new Error('An invalid device name or id was submitted');
}

return { name: message.attributes.deviceId, id: message.attributes.deviceNumId };
}
23 changes: 23 additions & 0 deletions udmif/event-handler/dist/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { EventFunction } from '@google-cloud/functions-framework/build/src/functions';
import { getDeviceDAO } from './DeviceDaoFactory';
import UdmiMessageHandler from './UdmiMessageHandler';

let messageHandler: UdmiMessageHandler;

/**
* Triggered from a message on a Cloud Pub/Sub topic.
*
* @param {!Object} event Event payload.
* @param {!Object} context Metadata for the event.
*/
export const handleUdmiEvent: EventFunction = async (event: any, context: any) => {
try {
if (!messageHandler) {
console.log('Creating Message Handler');
messageHandler = new UdmiMessageHandler(await getDeviceDAO());
}
messageHandler.handleUdmiEvent(event);
} catch (e) {
console.error('An unexpected error occurred: ', e);
}
};
39 changes: 39 additions & 0 deletions udmif/event-handler/dist/model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export interface DeviceKey {
name: string;
id: string;
}

export interface DeviceDocument {
name: string;
id: string;
lastPayload?: string;
make?: string;
model?: string;
operational?: string;
serialNumber?: string;
firmware?: string;
section?: string;
site?: string;
}

export interface UdmiMessage {
attributes: {
deviceId: string;
deviceNumId: string;
};
data: {
hardware?: {
make: string;
model: string;
};
location?: {
section: string;
site: string;
};
software?: any;
operational?: string;
serial_no?: string;
timestamp?: string;
};
}

39 changes: 39 additions & 0 deletions udmif/event-handler/dist/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "udmi-device-cf",
"version": "1.0.0",
"description": "Cloud Function that Receives Events and Writes Device Documents to MongoDB",
"author": "Buildings IOT",
"main": "dist/index.js",
"license": "ISC",
"dependencies": {
"mongodb": "^4.4.1"
},
"devDependencies": {
"@google-cloud/functions-framework": "^3.0.0",
"@shelf/jest-mongodb": "^2.2.1",
"@types/jest": "^27.4.0",
"@types/node": "^17.0.10",
"concurrently": "^7.1.0",
"husky": "^7.0.4",
"jest": "^27.4.7",
"jest-junit": "^13.0.0",
"lint-staged": "^12.2.2",
"nodemon": "^2.0.16",
"prettier": "^2.5.1",
"ts-jest": "^27.1.3",
"ts-node": "^10.4.0",
"typescript": "^4.5.5"
},
"scripts": {
"dev": "nodemon --watch src --watch data -e js,ts,txt,response,json --exec node -r ts-node/register --inspect=0.0.0.0:6329 src/index.ts",
"start": "functions-framework --source=dist/ --target=handleUdmiEvent --signature-type=event",
"watch": "concurrently \"tsc -w\" \"nodemon --watch ./build/ --exec npm run start\"",
"test": "NODE_ENV=test jest --runInBand --silent --detectOpenHandles",
"testInteractive": "NODE_ENV=test jest --runInBand --silent --watchAll",
"postinstall": "cd ../.. && husky install udmif/event-handler/.husky",
"build": "tsc -p ."
},
"lint-staged": {
"src/**/*.{ts,tsx,json}": "prettier --single-quote --arrow-parens always --trailing-comma es5 --print-width 120 --write"
}
}
Binary file added udmif/event-handler/index.zip
Binary file not shown.