Compare commits
2 Commits
4bbfdbd7ce
...
8aa1fa7b08
| Author | SHA1 | Date | |
|---|---|---|---|
| 8aa1fa7b08 | |||
| 6cc92a3ffb |
@ -26,15 +26,11 @@ export const InfoRequestSchema = z.object({
|
|||||||
export type InfoRequest = z.infer<typeof InfoRequestSchema>;
|
export type InfoRequest = z.infer<typeof InfoRequestSchema>;
|
||||||
export type InfoRequestTwilio = ServerlessEventObject<InfoRequest, UserAgentHeader> & BaseEvent;
|
export type InfoRequestTwilio = ServerlessEventObject<InfoRequest, UserAgentHeader> & BaseEvent;
|
||||||
|
|
||||||
export const InfoResponseClientSchema = DoorConfigSchema
|
export const InfoResponseSchema = DoorConfigSchema
|
||||||
.omit({ PK: true, SK: true, pin: true })
|
.omit({ PK: true, SK: true, pin: true })
|
||||||
.extend({ door: z.string() });
|
.extend({ door: z.string(), status: z.nativeEnum(DoorStatus), id: z.string() });
|
||||||
|
|
||||||
export type InfoResponseClient = z.infer<typeof InfoResponseClientSchema>;
|
export type InfoResponse = z.infer<typeof InfoResponseSchema>;
|
||||||
|
|
||||||
export const InfoResponseUISchema = InfoResponseClientSchema.extend({ id: z.string(), status: z.nativeEnum(DoorStatus) });
|
|
||||||
|
|
||||||
export type InfoResponseUI = z.infer<typeof InfoResponseUISchema>;
|
|
||||||
|
|
||||||
export const handler: ServerlessFunctionSignature<TwilioContext, InfoRequestTwilio> = withMetrics("info", async (context, event, callback, metricsRegistry) => {
|
export const handler: ServerlessFunctionSignature<TwilioContext, InfoRequestTwilio> = withMetrics("info", async (context, event, callback, metricsRegistry) => {
|
||||||
const response = new Twilio.Response();
|
const response = new Twilio.Response();
|
||||||
@ -65,32 +61,20 @@ export const handler: ServerlessFunctionSignature<TwilioContext, InfoRequestTwil
|
|||||||
setResponseJson(response, 404, {
|
setResponseJson(response, 404, {
|
||||||
msg: "This buzzer is not registered",
|
msg: "This buzzer is not registered",
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
if (buzzer) {
|
|
||||||
// respond to twilio CLIENT
|
|
||||||
const body: InfoResponseClient = InfoResponseClientSchema.parse({
|
|
||||||
...config,
|
|
||||||
buzzer,
|
|
||||||
door,
|
|
||||||
});
|
|
||||||
|
|
||||||
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: InfoResponse = InfoResponseSchema.parse({
|
||||||
...config,
|
...config,
|
||||||
id: door,
|
id: door,
|
||||||
door,
|
door,
|
||||||
status,
|
status,
|
||||||
})
|
});
|
||||||
|
|
||||||
// respond to UI
|
|
||||||
setResponseJson(response, 200, body);
|
setResponseJson(response, 200, body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// destroy the internal client after
|
// destroy the internal client after
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|||||||
@ -68,6 +68,7 @@ const doorConfig: DoorConfig = {
|
|||||||
SK: "config",
|
SK: "config",
|
||||||
buzzer: testDoorBuzzer,
|
buzzer: testDoorBuzzer,
|
||||||
pressKey: "4",
|
pressKey: "4",
|
||||||
|
// discordUsers: ["245290492760162304"],
|
||||||
discordUsers: [],
|
discordUsers: [],
|
||||||
fallbackNumbers: ["1231231234"],
|
fallbackNumbers: ["1231231234"],
|
||||||
pin: testDoorPin,
|
pin: testDoorPin,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { describe, test, expect } from "bun:test";
|
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 { InfoResponse } from "../src/functions/api/door/info";
|
||||||
import { ONBOARDING_DOOR_NAME, ONBOARDING_DOOR_PIN } from "../src/schema/DoorConfig";
|
import { ONBOARDING_DOOR_NAME, ONBOARDING_DOOR_PIN } from "../src/schema/DoorConfig";
|
||||||
import { StatusResponse } from "../src/functions/api/door/status";
|
import { StatusResponse } from "../src/functions/api/door/status";
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ describe("buzzer client works", () => {
|
|||||||
expect(buzzerActivatedResp).not.toContain("fail fast callback");
|
expect(buzzerActivatedResp).not.toContain("fail fast callback");
|
||||||
|
|
||||||
// door should now be closed
|
// door should now be closed
|
||||||
const infoResp = await fetch(baseUrl + `/api/door/info?door=${doorName}`).then(res => res.json()) as InfoResponseUI;
|
const infoResp = await fetch(baseUrl + `/api/door/info?door=${doorName}`).then(res => res.json()) as InfoResponse;
|
||||||
expect(infoResp.status).toBe(DoorStatus.CLOSED);
|
expect(infoResp.status).toBe(DoorStatus.CLOSED);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -48,7 +48,7 @@ describe("onboardingflag door should exist", () => {
|
|||||||
const authResp = await fetch(baseUrl + `/api/door/info?door=${ONBOARDING_DOOR_NAME}`);
|
const authResp = await fetch(baseUrl + `/api/door/info?door=${ONBOARDING_DOOR_NAME}`);
|
||||||
expect(authResp.status).toBe(200);
|
expect(authResp.status).toBe(200);
|
||||||
|
|
||||||
const door = (await authResp.json()) as InfoResponseUI;
|
const door = (await authResp.json()) as InfoResponse;
|
||||||
expect(door.timeout).toBe(15 * 60);
|
expect(door.timeout).toBe(15 * 60);
|
||||||
|
|
||||||
// this may be flaky, it would fail when a user is onboarding and we are deploying
|
// this may be flaky, it would fail when a user is onboarding and we are deploying
|
||||||
@ -59,11 +59,11 @@ describe("onboardingflag door should exist", () => {
|
|||||||
let authResp = await fetch(baseUrl + `/api/door/auth?door=${ONBOARDING_DOOR_NAME}&key=${ONBOARDING_DOOR_PIN}`);
|
let authResp = await fetch(baseUrl + `/api/door/auth?door=${ONBOARDING_DOOR_NAME}&key=${ONBOARDING_DOOR_PIN}`);
|
||||||
expect(authResp.status).toBe(200);
|
expect(authResp.status).toBe(200);
|
||||||
|
|
||||||
let statusResp = await fetch(baseUrl + `/api/door/info?door=${ONBOARDING_DOOR_NAME}`).then(res => res.json()) as InfoResponseUI;
|
let statusResp = await fetch(baseUrl + `/api/door/info?door=${ONBOARDING_DOOR_NAME}`).then(res => res.json()) as InfoResponse;
|
||||||
expect(statusResp.status).toBe(DoorStatus.OPEN);
|
expect(statusResp.status).toBe(DoorStatus.OPEN);
|
||||||
|
|
||||||
// open again
|
// open again
|
||||||
statusResp = await fetch(baseUrl + `/api/door/info?door=${ONBOARDING_DOOR_NAME}`).then(res => res.json()) as InfoResponseUI;
|
statusResp = await fetch(baseUrl + `/api/door/info?door=${ONBOARDING_DOOR_NAME}`).then(res => res.json()) as InfoResponse;
|
||||||
expect(statusResp.status).toBe(DoorStatus.OPEN);
|
expect(statusResp.status).toBe(DoorStatus.OPEN);
|
||||||
|
|
||||||
// close it
|
// close it
|
||||||
@ -71,7 +71,7 @@ describe("onboardingflag door should exist", () => {
|
|||||||
expect(authResp.status).toBe(200);
|
expect(authResp.status).toBe(200);
|
||||||
|
|
||||||
// door should now be closed
|
// door should now be closed
|
||||||
const infoResp = await fetch(baseUrl + `/api/door/info?door=${ONBOARDING_DOOR_NAME}`).then(res => res.json()) as InfoResponseUI;
|
const infoResp = await fetch(baseUrl + `/api/door/info?door=${ONBOARDING_DOOR_NAME}`).then(res => res.json()) as InfoResponse;
|
||||||
expect(infoResp.status).toBe(DoorStatus.CLOSED);
|
expect(infoResp.status).toBe(DoorStatus.CLOSED);
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import { TwilioContext } from '../types/TwilioContext';
|
|||||||
import VoiceResponse from 'twilio/lib/twiml/VoiceResponse';
|
import VoiceResponse from 'twilio/lib/twiml/VoiceResponse';
|
||||||
import { DoorStatus } from '../../../doorman-api/src/types/DoorStatus';
|
import { DoorStatus } from '../../../doorman-api/src/types/DoorStatus';
|
||||||
import { StatusResponse } from '../../../doorman-api/src/functions/api/door/status';
|
import { StatusResponse } from '../../../doorman-api/src/functions/api/door/status';
|
||||||
import { InfoResponseClient } from '../../../doorman-api/src/functions/api/door/info';
|
import { InfoResponse } from '../../../doorman-api/src/functions/api/door/info';
|
||||||
import { getMetricFromRegistry, withMetrics } from '../../../doorman-api/src/common/DoormanHandler';
|
import { getMetricFromRegistry, withMetrics } from '../../../doorman-api/src/common/DoormanHandler';
|
||||||
import { Counter, Summary } from 'prom-client';
|
import { Counter, Summary } from 'prom-client';
|
||||||
import { BuzzerActivatedMetrics, registerMetrics } from '../metrics/BuzzerActivatedMetrics';
|
import { BuzzerActivatedMetrics, registerMetrics } from '../metrics/BuzzerActivatedMetrics';
|
||||||
@ -27,7 +27,7 @@ export const handler: ServerlessFunctionSignature<TwilioContext, BuzzerDialEvent
|
|||||||
|
|
||||||
let invokeId = `[${randomUUID()}]`;
|
let invokeId = `[${randomUUID()}]`;
|
||||||
let configString = event.config;
|
let configString = event.config;
|
||||||
let config: InfoResponseClient | undefined;
|
let config: InfoResponse | undefined;
|
||||||
console.log(invokeId + " starting execution");
|
console.log(invokeId + " starting execution");
|
||||||
|
|
||||||
// get by api or parse it out from query
|
// get by api or parse it out from query
|
||||||
@ -69,8 +69,10 @@ export const handler: ServerlessFunctionSignature<TwilioContext, BuzzerDialEvent
|
|||||||
}
|
}
|
||||||
|
|
||||||
// let users know someone is currently buzzing, and allow unlock by discord user
|
// let users know someone is currently buzzing, and allow unlock by discord user
|
||||||
let msg = `🔔 Someone is dialing right now @ Door "${config.door}" [Click to unlock](${context.DOORMAN_URL}/api/door/auth?door=${config.door}&key=`;
|
let msg = `🔔 Someone is dialing now @ Door "${config.door}"\n\n`;
|
||||||
let msgs = config.discordUsers.map((u) =>
|
|
||||||
|
msg += `Will dial fallback numbers if door remains locked\n[Click to unlock](${context.DOORMAN_URL}/api/door/auth?door=${config.door}&key=`;
|
||||||
|
let msgs = config.discordUsers.map((u: string) =>
|
||||||
msg + u + ')'
|
msg + u + ')'
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -91,7 +93,6 @@ export const handler: ServerlessFunctionSignature<TwilioContext, BuzzerDialEvent
|
|||||||
const recordPollLatency = getMetricFromRegistry<Summary>(metricsRegistry, BuzzerActivatedMetrics.POLL_LATENCY)
|
const recordPollLatency = getMetricFromRegistry<Summary>(metricsRegistry, BuzzerActivatedMetrics.POLL_LATENCY)
|
||||||
.startTimer({ door: config.door });
|
.startTimer({ door: config.door });
|
||||||
|
|
||||||
|
|
||||||
// prevent multiple polling at once
|
// prevent multiple polling at once
|
||||||
if (pollLock) {
|
if (pollLock) {
|
||||||
return;
|
return;
|
||||||
@ -140,12 +141,6 @@ export const handler: ServerlessFunctionSignature<TwilioContext, BuzzerDialEvent
|
|||||||
console.log(
|
console.log(
|
||||||
invokeId + " GracefulFallbackPromise: I was the fastest, so I will attempt to notify discord users before resolving with a call"
|
invokeId + " GracefulFallbackPromise: I was the fastest, so I will attempt to notify discord users before resolving with a call"
|
||||||
);
|
);
|
||||||
await notifyAllDiscord(
|
|
||||||
context,
|
|
||||||
config,
|
|
||||||
`📞 Somebody buzzed the door and it dialed through to fallback phone numbers @ Door "${config.door}"`,
|
|
||||||
metricsRegistry,
|
|
||||||
);
|
|
||||||
resolve(twiml);
|
resolve(twiml);
|
||||||
} else {
|
} else {
|
||||||
console.log(
|
console.log(
|
||||||
|
|||||||
@ -1,17 +1,17 @@
|
|||||||
import { register, Registry, Summary } from "prom-client";
|
import { register, Registry, Summary } from "prom-client";
|
||||||
import { InfoResponseClient, InfoResponseUI } from "../../../doorman-api/src/functions/api/door/info";
|
import { InfoResponse } from "../../../doorman-api/src/functions/api/door/info";
|
||||||
import { TwilioContext } from "../types/TwilioContext";
|
import { TwilioContext } from "../types/TwilioContext";
|
||||||
import { lambdaFastHttp } from "./LambdaUtils";
|
import { lambdaFastHttp } from "./LambdaUtils";
|
||||||
import { BuzzerActivatedMetrics } from "../metrics/BuzzerActivatedMetrics";
|
import { BuzzerActivatedMetrics } from "../metrics/BuzzerActivatedMetrics";
|
||||||
import { ONBOARDING_DOOR_NAME } from "../../../doorman-api/src/schema/DoorConfig";
|
import { ONBOARDING_DOOR_NAME } from "../../../doorman-api/src/schema/DoorConfig";
|
||||||
import { LogCallResponse } from "../../../doorman-api/src/functions/api/door/logCall";
|
import { LogCallResponse } from "../../../doorman-api/src/functions/api/door/logCall";
|
||||||
|
|
||||||
export async function getConfig(context: TwilioContext, buzzer: string): Promise<InfoResponseClient | undefined> {
|
export async function getConfig(context: TwilioContext, buzzer: string): Promise<InfoResponse | undefined> {
|
||||||
return await fetch(context.DOORMAN_URL + `/api/door/info?buzzer=${buzzer}`)
|
return await fetch(context.DOORMAN_URL + `/api/door/info?buzzer=${buzzer}`)
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
return undefined;
|
return undefined;
|
||||||
}) as InfoResponseClient;
|
}) as InfoResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function tryLogCallerForOnboarding(context: TwilioContext, caller: string): Promise<LogCallResponse> {
|
export async function tryLogCallerForOnboarding(context: TwilioContext, caller: string): Promise<LogCallResponse> {
|
||||||
@ -31,6 +31,6 @@ export async function notifyDiscord(context: TwilioContext, msg: string[], u: st
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function notifyAllDiscord(context: TwilioContext, config: InfoResponseClient, msg: string, metricsRegistry: Registry, optionalJsonStr: string = "") {
|
export async function notifyAllDiscord(context: TwilioContext, config: InfoResponse, msg: string, metricsRegistry: Registry, optionalJsonStr: string = "") {
|
||||||
return notifyDiscord(context, config.discordUsers.map(() => msg), config.discordUsers, config.discordUsers.map(() => optionalJsonStr), metricsRegistry);
|
return notifyDiscord(context, config.discordUsers.map(() => msg), config.discordUsers, config.discordUsers.map(() => optionalJsonStr), metricsRegistry);
|
||||||
}
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import VoiceResponse from 'twilio/lib/twiml/VoiceResponse';
|
import VoiceResponse from 'twilio/lib/twiml/VoiceResponse';
|
||||||
import { InfoResponseClient } from '../../../doorman-api/src/functions/api/door/info';
|
import { InfoResponse } from '../../../doorman-api/src/functions/api/door/info';
|
||||||
|
|
||||||
export function doorOpenTwiml(config: InfoResponseClient): VoiceResponse {
|
export function doorOpenTwiml(config: InfoResponse): VoiceResponse {
|
||||||
const twiml = new Twilio.twiml.VoiceResponse();
|
const twiml = new Twilio.twiml.VoiceResponse();
|
||||||
|
|
||||||
// play audio
|
// play audio
|
||||||
@ -20,7 +20,7 @@ export function doorOpenTwiml(config: InfoResponseClient): VoiceResponse {
|
|||||||
return twiml;
|
return twiml;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function dialFallbackTwiml(config: InfoResponseClient): VoiceResponse {
|
export function dialFallbackTwiml(config: InfoResponse): VoiceResponse {
|
||||||
const twiml = new Twilio.twiml.VoiceResponse();
|
const twiml = new Twilio.twiml.VoiceResponse();
|
||||||
|
|
||||||
let dial = twiml.dial({
|
let dial = twiml.dial({
|
||||||
@ -67,4 +67,3 @@ export function doorOnboardTwiml(otp: string): VoiceResponse {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return twiml;
|
return twiml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user