add hardcoded door "onboardingflag" for automated onboarding process
This commit is contained in:
parent
512feb0268
commit
33d7764ca2
10
packages/doorman-api/src/common/TTLHelper.ts
Normal file
10
packages/doorman-api/src/common/TTLHelper.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export function isTTLInFuture(item?: { TTL: number }) {
|
||||||
|
if (!item) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ttl is a UTC ms time
|
||||||
|
const ttl = item.TTL || 0;
|
||||||
|
|
||||||
|
return parseInt("" + ttl) > Date.now();
|
||||||
|
}
|
||||||
@ -18,8 +18,8 @@ export const InfoRequestSchema = z.object({
|
|||||||
door: z.string().optional(),
|
door: z.string().optional(),
|
||||||
buzzer: z.string().optional(),
|
buzzer: z.string().optional(),
|
||||||
})
|
})
|
||||||
.partial()
|
.partial()
|
||||||
.refine(data => data.buzzer || data.door, 'Buzzer or door must be provided');
|
.refine(data => data.buzzer || data.door, 'Buzzer or door must be provided');
|
||||||
|
|
||||||
export type InfoRequest = z.infer<typeof InfoRequestSchema>;
|
export type InfoRequest = z.infer<typeof InfoRequestSchema>;
|
||||||
export interface InfoRequestTwilio extends ServerlessEventObject<InfoRequest, UserAgentHeader> { };
|
export interface InfoRequestTwilio extends ServerlessEventObject<InfoRequest, UserAgentHeader> { };
|
||||||
@ -75,7 +75,7 @@ export const handler: ServerlessFunctionSignature<TwilioContext, InfoRequestTwil
|
|||||||
setResponseJson(response, 200, body);
|
setResponseJson(response, 200, body);
|
||||||
} else {
|
} else {
|
||||||
const lock = await db.entities.lockStatus.findById(getLockStatusID(door));
|
const lock = await db.entities.lockStatus.findById(getLockStatusID(door));
|
||||||
const status = isLockOpen(lock) ? DoorStatus.OPEN: DoorStatus.CLOSED;
|
const status = isLockOpen(lock) ? DoorStatus.OPEN : DoorStatus.CLOSED;
|
||||||
|
|
||||||
const body: InfoResponseUI = InfoResponseUISchema.parse({
|
const body: InfoResponseUI = InfoResponseUISchema.parse({
|
||||||
...config,
|
...config,
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import { withMetrics } from "../../../common/DoormanHandler";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { setResponseJson } from "../../../utils/responseUtils";
|
import { setResponseJson } from "../../../utils/responseUtils";
|
||||||
import zu from "zod_utilz";
|
import zu from "zod_utilz";
|
||||||
|
import { ONBOARDING_DOOR_NAME } from "../../../schema/DoorConfig";
|
||||||
|
|
||||||
export const StatusRequestSchema = z.object({
|
export const StatusRequestSchema = z.object({
|
||||||
door: z.string(),
|
door: z.string(),
|
||||||
@ -49,7 +50,10 @@ export const handler: ServerlessFunctionSignature<TwilioContext, StatusRequestTw
|
|||||||
|
|
||||||
setResponseJson(response, 200, body);
|
setResponseJson(response, 200, body);
|
||||||
|
|
||||||
|
// this hardcoded door, keep open for timeout
|
||||||
|
if (door !== ONBOARDING_DOOR_NAME) {
|
||||||
await db.entities.lockStatus.deleteById(getLockStatusID(door));
|
await db.entities.lockStatus.deleteById(getLockStatusID(door));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const body = StatusResponseSchema.parse({
|
const body = StatusResponseSchema.parse({
|
||||||
status: DoorStatus.CLOSED,
|
status: DoorStatus.CLOSED,
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { readdirSync } from "node:fs";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
const paths = [
|
const paths = [
|
||||||
'./src/functions/api/door'
|
'./src/functions/api/door',
|
||||||
];
|
];
|
||||||
|
|
||||||
const functionFiles = paths.map(path => readdirSync(path).map(file => path + "/" + file)).flat();
|
const functionFiles = paths.map(path => readdirSync(path).map(file => path + "/" + file)).flat();
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { CreateTableCommand, DynamoDBClient, KeyType, PutItemCommand, ScalarAttributeType } from "@aws-sdk/client-dynamodb";
|
import { CreateTableCommand, DynamoDBClient, KeyType, PutItemCommand, ScalarAttributeType } from "@aws-sdk/client-dynamodb";
|
||||||
import { DynamoDBDocument } from "@aws-sdk/lib-dynamodb";
|
import { DynamoDBDocument } from "@aws-sdk/lib-dynamodb";
|
||||||
import { DoorAlias, DoorAliasEntity, DoorAliasSchema, getDoorAliasID } from "../schema/DoorAlias";
|
import { DoorAlias, DoorAliasEntity, DoorAliasSchema, getDoorAliasID } from "../schema/DoorAlias";
|
||||||
import { DoorConfig } from "../schema/DoorConfig";
|
import { DoorConfig, getDoorConfigID, ONBOARDING_DOOR_NAME, ONBOARDING_DOOR_PIN } from "../schema/DoorConfig";
|
||||||
|
|
||||||
import DynamoDbLocal from "dynamodb-local";
|
import DynamoDbLocal from "dynamodb-local";
|
||||||
import { sleep } from "bun";
|
import { sleep } from "bun";
|
||||||
@ -76,6 +76,20 @@ const doorConfig: DoorConfig = {
|
|||||||
greeting: "test door",
|
greeting: "test door",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// for onboarding mode
|
||||||
|
const onboardingDoorConfig: DoorConfig = {
|
||||||
|
PK: "door-" + ONBOARDING_DOOR_NAME,
|
||||||
|
SK: "config",
|
||||||
|
buzzer: "nil",
|
||||||
|
pressKey: "nil",
|
||||||
|
discordUsers: [],
|
||||||
|
fallbackNumbers: [],
|
||||||
|
pin: ONBOARDING_DOOR_PIN,
|
||||||
|
buzzerCode: "nil",
|
||||||
|
timeout: 15 * 60,
|
||||||
|
greeting: "INTERNAL USE - for onboarding new door",
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await document.put({
|
await document.put({
|
||||||
TableName: tableName,
|
TableName: tableName,
|
||||||
@ -86,6 +100,12 @@ try {
|
|||||||
TableName: tableName,
|
TableName: tableName,
|
||||||
Item: doorAlias,
|
Item: doorAlias,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await document.put({
|
||||||
|
TableName: tableName,
|
||||||
|
Item: onboardingDoorConfig,
|
||||||
|
});
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("failed seeding table", e);
|
console.error("failed seeding table", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,9 @@ import { randomUUID } from "crypto";
|
|||||||
export const DOOR_CONFIG_SK = "config";
|
export const DOOR_CONFIG_SK = "config";
|
||||||
export const EDIT_DOOR_CONFIG_SK = "config-update";
|
export const EDIT_DOOR_CONFIG_SK = "config-update";
|
||||||
|
|
||||||
|
export const ONBOARDING_DOOR_NAME = "onboardingflag";
|
||||||
|
export const ONBOARDING_DOOR_PIN = "1234";
|
||||||
|
|
||||||
export const DoorConfigSchema = z.object({
|
export const DoorConfigSchema = z.object({
|
||||||
// keys
|
// keys
|
||||||
PK: z.string().startsWith("door-", "Invalid door key"),
|
PK: z.string().startsWith("door-", "Invalid door key"),
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { DynaBridgeEntity } from 'dynabridge';
|
import { DynaBridgeEntity } from 'dynabridge';
|
||||||
|
import { isTTLInFuture } from "../common/TTLHelper";
|
||||||
|
|
||||||
export const LOCK_STATUS_SK = "lock";
|
export const LOCK_STATUS_SK = "lock";
|
||||||
|
|
||||||
@ -24,10 +25,7 @@ export const LockStatusEntity: DynaBridgeEntity<LockStatus> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const isLockOpen = (lock?: LockStatus) => {
|
export const isLockOpen = (lock?: LockStatus) => {
|
||||||
// ttl is a UTC ms time for how long it is unlocked
|
return isTTLInFuture(lock);
|
||||||
const ttl = lock?.TTL || 0;
|
|
||||||
|
|
||||||
return parseInt("" + ttl) > Date.now();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createLockStatusWithTimeout = (door: string, timeoutSeconds: number, fingerprint: any): LockStatus => {
|
export const createLockStatusWithTimeout = (door: string, timeoutSeconds: number, fingerprint: any): LockStatus => {
|
||||||
|
|||||||
@ -2,6 +2,8 @@ import { describe, test, expect } from "bun:test";
|
|||||||
import { buzzerUrl, buzzerNumber, baseUrl, doorName, key } from "./testCommon";
|
import { buzzerUrl, buzzerNumber, baseUrl, doorName, key } from "./testCommon";
|
||||||
import { DoorStatus } from "../src/types/DoorStatus";
|
import { DoorStatus } from "../src/types/DoorStatus";
|
||||||
import { InfoResponseUI } from "../src/functions/api/door/info";
|
import { InfoResponseUI } from "../src/functions/api/door/info";
|
||||||
|
import { ONBOARDING_DOOR_NAME, ONBOARDING_DOOR_PIN } from "../src/schema/DoorConfig";
|
||||||
|
import { StatusResponse } from "../src/functions/api/door/status";
|
||||||
|
|
||||||
if (process.env.STAGE !== 'staging') {
|
if (process.env.STAGE !== 'staging') {
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
@ -33,3 +35,38 @@ describe("buzzer client works", () => {
|
|||||||
expect(infoResp.status).toBe(DoorStatus.CLOSED);
|
expect(infoResp.status).toBe(DoorStatus.CLOSED);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// CAVEAT, this door is currently manually created, for the purposes of automated onboarding flow
|
||||||
|
describe("onboardingflag door should exist", () => {
|
||||||
|
test("onboarding door exists", async () => {
|
||||||
|
// door info
|
||||||
|
const authResp = await fetch(baseUrl + `/api/door/info?door=${ONBOARDING_DOOR_NAME}`);
|
||||||
|
expect(authResp.status).toBe(200);
|
||||||
|
|
||||||
|
const door = (await authResp.json()) as InfoResponseUI;
|
||||||
|
expect(door.timeout).toBe(15 * 60);
|
||||||
|
|
||||||
|
// this may be flaky, it would fail when a user is onboarding and we are deploying
|
||||||
|
expect(door.status).toBe(DoorStatus.CLOSED);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("onboarding door does not close after status check", async () => {
|
||||||
|
let authResp = await fetch(baseUrl + `/api/door/auth?door=${ONBOARDING_DOOR_NAME}&key=${ONBOARDING_DOOR_PIN}`);
|
||||||
|
expect(authResp.status).toBe(200);
|
||||||
|
|
||||||
|
let statusResp = await fetch(baseUrl + `/api/door/status?door=${ONBOARDING_DOOR_NAME}`).then(res => res.json()) as StatusResponse;
|
||||||
|
expect(statusResp.status).toBe(DoorStatus.OPEN);
|
||||||
|
|
||||||
|
// open again
|
||||||
|
statusResp = await fetch(baseUrl + `/api/door/status?door=${ONBOARDING_DOOR_NAME}`).then(res => res.json()) as StatusResponse;
|
||||||
|
expect(statusResp.status).toBe(DoorStatus.OPEN);
|
||||||
|
|
||||||
|
// close it
|
||||||
|
authResp = await fetch(baseUrl + `/api/door/auth?door=${ONBOARDING_DOOR_NAME}&key=${ONBOARDING_DOOR_PIN}`);
|
||||||
|
expect(authResp.status).toBe(200);
|
||||||
|
|
||||||
|
// door should now be closed
|
||||||
|
const infoResp = await fetch(baseUrl + `/api/door/info?door=${ONBOARDING_DOOR_NAME}`).then(res => res.json()) as InfoResponseUI;
|
||||||
|
expect(infoResp.status).toBe(DoorStatus.CLOSED);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user