Skip to content
This repository has been archived by the owner on Feb 17, 2021. It is now read-only.

Commit

Permalink
feat: expose Cosmos DB query interface
Browse files Browse the repository at this point in the history
  • Loading branch information
coderbyheart committed Jun 15, 2020
1 parent 92fc133 commit 10aa574
Show file tree
Hide file tree
Showing 11 changed files with 8,068 additions and 2,786 deletions.
1 change: 1 addition & 0 deletions .github/workflows/test-and-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ jobs:
${{ runner.OS }}-
- name: Install dependencies
run: npm ci --no-audit
- run: npm test
- name: Semantic release
if: success()
continue-on-error: true
Expand Down
2 changes: 1 addition & 1 deletion lib/iotMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ export type DeviceMessage = {
}
}

export type Update = TwinChangeEvent | DeviceMessage
export type DeviceUpdate = TwinChangeEvent | DeviceMessage
10,716 changes: 7,945 additions & 2,771 deletions package-lock.json

Large diffs are not rendered by default.

31 changes: 27 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"scripts": {
"postinstall": "check-node-version --package",
"snyk-protect": "snyk protect",
"prepare": "npm run snyk-protect"
"prepare": "npm run snyk-protect",
"test": "jest"
},
"repository": {
"type": "git",
Expand All @@ -26,17 +27,18 @@
"license": "BSD-3-Clause",
"dependencies": {
"@azure/arm-iothub": "^3.0.0",
"@azure/cosmos": "^3.7.1",
"@azure/ms-rest-azure-js": "^2.0.1",
"@azure/ms-rest-js": "^2.0.7",
"@azure/ms-rest-nodeauth": ">=2.0.5 <3.0.0",
"@azure/storage-blob": "^12.1.2",
"@bifravst/device-ui-server": "^1.3.1",
"@bifravst/device-ui-server": "^1.3.2",
"@bifravst/random-words": "^4.2.0",
"azure-iot-provisioning-service": "^1.8.3",
"azure-iothub": "^1.12.3",
"deepmerge": "^4.1.1",
"mqtt": "^4.1.0",
"snyk": "^1.341.0",
"snyk": "^1.341.1",
"uuid": "^8.1.0"
},
"devDependencies": {
Expand All @@ -45,14 +47,17 @@
"@bifravst/code-style": "^8.0.1",
"@commitlint/cli": "^8.3.5",
"@commitlint/config-angular": "^8.3.4",
"@types/jest": "^26.0.0",
"@types/jsonwebtoken": "^8.5.0",
"@types/node": "^14.0.13",
"@types/pem": "^1.9.5",
"@types/uuid": "^8.0.0",
"check-node-version": "^4.0.3",
"husky": "^4.2.5",
"jest": "^26.0.1",
"pem": "^1.14.4",
"run-node": "^2.0.0",
"ts-jest": "^26.1.0",
"typescript": "^3.9.5"
},
"husky": {
Expand Down Expand Up @@ -85,5 +90,23 @@
"@semantic-release/github"
]
},
"snyk": true
"snyk": true,
"jest": {
"testURL": "http://localhost",
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"json"
],
"transform": {
"^.+\\.tsx?$": "ts-jest"
},
"testRegex": ".+\\.spec\\.ts$",
"globals": {
"ts-jest": {
"diagnostics": true
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { AzureFunction, Context } from '@azure/functions'
import { log } from '../lib/log'
import { Update, TwinChangeEvent } from '../lib/iotMessages'
import { DeviceUpdate, TwinChangeEvent } from '../lib/iotMessages'

/**
* Publishes Device Twin Update to SignalR so the web application can receive real-time notifications
*/
const publishDeviceUpdatesToSignalR: AzureFunction = async (
context: Context,
updates: Update[],
updates: DeviceUpdate[],
): Promise<void> => {
log(context)({
messages: updates,
Expand All @@ -16,7 +16,7 @@ const publishDeviceUpdatesToSignalR: AzureFunction = async (

const signalRMessages = []

const addProperties = (message: Update, k: number) => ({
const addProperties = (message: DeviceUpdate, k: number) => ({
message,
systemProperties: context.bindingData.systemPropertiesArray[k],
})
Expand Down
18 changes: 18 additions & 0 deletions queryHistoricalDeviceData/function.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"bindings": [
{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": ["post"],
"route": "history"
},
{
"type": "http",
"direction": "out",
"name": "res"
}
],
"scriptFile": "../dist/queryHistoricalDeviceData/queryHistoricalDeviceData.js"
}
14 changes: 14 additions & 0 deletions queryHistoricalDeviceData/parseConnectionString.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { parseConnectionString } from './parseConnectionString'

describe('parseConnectionString', () => {
it('should parse a connection string', () => {
expect(
parseConnectionString(
'AccountEndpoint=https://xxxx.documents.azure.com:443/;AccountKey=oKHTAxxx92GKkq3CDzeCd1WYnVslfIUaQqOa7Xw==;',
),
).toEqual({
AccountEndpoint: 'https://xxxx.documents.azure.com:443/',
AccountKey: 'oKHTAxxx92GKkq3CDzeCd1WYnVslfIUaQqOa7Xw==',
})
})
})
14 changes: 14 additions & 0 deletions queryHistoricalDeviceData/parseConnectionString.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const parseConnectionString = (
connectionString: string,
): { [key: string]: string } =>
connectionString
.replace(/;$/, '')
.split(';')
.reduce((conn, s) => {
const [k] = s.split('=', 1)
const v = s.replace(new RegExp(`^${k}=`), '')
return {
...conn,
[k]: v,
}
}, {} as { [key: string]: string })
38 changes: 38 additions & 0 deletions queryHistoricalDeviceData/queryHistoricalDeviceData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { AzureFunction, Context, HttpRequest } from '@azure/functions'
import { log } from '../lib/log'
import { r } from '../lib/http'
import { CosmosClient } from '@azure/cosmos'
import { parseConnectionString } from './parseConnectionString'

const { AccountEndpoint, AccountKey } = parseConnectionString(
process.env.HISTORICAL_DATA_COSMOSDB_CONNECTION_STRING ?? '',
)
const cosmosClient = new CosmosClient({
endpoint: AccountEndpoint,
key: AccountKey,
})

const container = cosmosClient.database('deviceMessages').container('updates')

/**
* Query historical device updates stored in Cosmos DB
*/
const queryHistoricalDeviceData: AzureFunction = async (
context: Context,
req: HttpRequest,
): Promise<void> => {
log(context)({ req })
try {
const result = {
result: (await container.items.query(req.body.query).fetchAll())
.resources,
}
log(context)({ result })
context.res = r(result)
} catch (error) {
console.error({ error })
context.res = r(error, 500)
}
}

export default queryHistoricalDeviceData
4 changes: 2 additions & 2 deletions storeDeviceUpdateInCosmosDB/function.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
{
"bindings": [
{
"name": "update",
"type": "eventHubTrigger",
"direction": "in",
"name": "update",
"eventHubName": "%IoTHubEventHubName%",
"cardinality": "one",
"connection": "IoTHubEventHubCompatibleConnectionString",
"consumerGroup": "storedeviceupdate"
},
{
"name": "deviceMessage",
"name": "deviceUpdate",
"type": "cosmosDB",
"databaseName": "deviceMessages",
"collectionName": "updates",
Expand Down
10 changes: 5 additions & 5 deletions storeDeviceUpdateInCosmosDB/storeDeviceUpdateInCosmosDB.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { AzureFunction, Context } from '@azure/functions'
import { log } from '../lib/log'
import { Update } from '../lib/iotMessages'
import { DeviceUpdate } from '../lib/iotMessages'

/**
* Store Device Twin Update in Cosmose DB SignalR so it can be queried later
* Store Device Twin Update in Cosmos DB so it can be queried later
*/
const storeDeviceUpdateInCosmosDB: AzureFunction = async (
context: Context,
update: Update,
update: DeviceUpdate,
): Promise<void> => {
const doc = {
update,
deviceUpdate: update,
deviceId:
context.bindingData.systemProperties['iothub-connection-device-id'],
timestamp: context.bindingData.systemProperties['iothub-enqueuedtime'],
Expand All @@ -19,7 +19,7 @@ const storeDeviceUpdateInCosmosDB: AzureFunction = async (

log(context)(doc)

context.bindings.deviceMessage = JSON.stringify(doc)
context.bindings.deviceUpdate = JSON.stringify(doc)

context.done()
}
Expand Down

0 comments on commit 10aa574

Please sign in to comment.