Skip to content

Commit

Permalink
refactor!: use a union type for SignatureType (#331)
Browse files Browse the repository at this point in the history
This commit refactors the SignatureType enum into an array of strings
declared [as const](microsoft/TypeScript#29510).
This allows the SignatureType type to be expressed as a union type,
which works a bit better when parsing a users provided string.

This is some simple refactoring in preparation for declarative function
signatures.

BREAKING CHANGE: exported SignatureType type is converted from an enum
to a union type
  • Loading branch information
matthewrobertson committed Sep 23, 2021
1 parent 30bb77d commit 9cf46ed
Show file tree
Hide file tree
Showing 7 changed files with 37 additions and 33 deletions.
25 changes: 9 additions & 16 deletions src/index.ts
Expand Up @@ -36,7 +36,7 @@ import {resolve} from 'path';
import {getUserFunction} from './loader';
import {ErrorHandler} from './invoker';
import {getServer} from './server';
import {SignatureType} from './types';
import {SignatureType, isValidSignatureType} from './types';

// Supported command-line flags
const FLAG = {
Expand All @@ -54,10 +54,6 @@ const ENV = {
SOURCE: 'FUNCTION_SOURCE',
};

enum NodeEnv {
PRODUCTION = 'production',
}

const argv = minimist(process.argv, {
string: [FLAG.PORT, FLAG.TARGET, FLAG.SIGNATURE_TYPE],
});
Expand All @@ -68,17 +64,14 @@ const CODE_LOCATION = resolve(
const PORT = argv[FLAG.PORT] || process.env[ENV.PORT] || '8080';
const TARGET = argv[FLAG.TARGET] || process.env[ENV.TARGET] || 'function';

const SIGNATURE_TYPE_STRING =
argv[FLAG.SIGNATURE_TYPE] || process.env[ENV.SIGNATURE_TYPE] || 'http';
const SIGNATURE_TYPE =
SignatureType[
SIGNATURE_TYPE_STRING.toUpperCase() as keyof typeof SignatureType
];
if (SIGNATURE_TYPE === undefined) {
const SIGNATURE_TYPE = (
argv[FLAG.SIGNATURE_TYPE] ||
process.env[ENV.SIGNATURE_TYPE] ||
'http'
).toLowerCase();
if (!isValidSignatureType(SIGNATURE_TYPE)) {
console.error(
`Function signature type must be one of: ${Object.values(
SignatureType
).join(', ')}.`
`Function signature type must be one of: ${SignatureType.join(', ')}.`
);
// eslint-disable-next-line no-process-exit
process.exit(1);
Expand Down Expand Up @@ -108,7 +101,7 @@ getUserFunction(CODE_LOCATION, TARGET).then(userFunction => {

SERVER.listen(PORT, () => {
ERROR_HANDLER.register();
if (process.env.NODE_ENV !== NodeEnv.PRODUCTION) {
if (process.env.NODE_ENV !== 'production') {
console.log('Serving function...');
console.log(`Function: ${TARGET}`);
console.log(`Signature type: ${SIGNATURE_TYPE}`);
Expand Down
4 changes: 2 additions & 2 deletions src/router.ts
Expand Up @@ -39,7 +39,7 @@ export function registerFunctionRoutes(
userFunction: HandlerFunction,
functionSignatureType: SignatureType
) {
if (functionSignatureType === SignatureType.HTTP) {
if (functionSignatureType === 'http') {
app.use('/favicon.ico|/robots.txt', (req, res) => {
// Neither crawlers nor browsers attempting to pull the icon find the body
// contents particularly useful, so we send nothing in the response body.
Expand All @@ -57,7 +57,7 @@ export function registerFunctionRoutes(
const handler = makeHttpHandler(userFunction as HttpFunction);
handler(req, res, next);
});
} else if (functionSignatureType === SignatureType.EVENT) {
} else if (functionSignatureType === 'event') {
app.post('/*', (req, res, next) => {
const wrappedUserFunction = wrapEventFunction(
userFunction as EventFunction | EventFunctionWithCallback
Expand Down
8 changes: 4 additions & 4 deletions src/server.ts
Expand Up @@ -101,19 +101,19 @@ export function getServer(
app.disable('x-powered-by');

if (
functionSignatureType === SignatureType.EVENT ||
functionSignatureType === SignatureType.CLOUDEVENT
functionSignatureType === 'event' ||
functionSignatureType === 'cloudevent'
) {
// If a Pub/Sub subscription is configured to invoke a user's function directly, the request body
// needs to be marshalled into the structure that wrapEventFunction expects. This unblocks local
// development with the Pub/Sub emulator
app.use(legacyPubSubEventMiddleware);
}

if (functionSignatureType === SignatureType.EVENT) {
if (functionSignatureType === 'event') {
app.use(cloudeventToBackgroundEventMiddleware);
}
if (functionSignatureType === SignatureType.CLOUDEVENT) {
if (functionSignatureType === 'cloudevent') {
app.use(backgroundEventToCloudEventMiddleware);
}

Expand Down
24 changes: 19 additions & 5 deletions src/types.ts
Expand Up @@ -16,8 +16,22 @@
// executing the client function.
export const FUNCTION_STATUS_HEADER_FIELD = 'X-Google-Status';

export enum SignatureType {
HTTP = 'http',
EVENT = 'event',
CLOUDEVENT = 'cloudevent',
}
/**
* List of function signature types that are supported by the framework.
*/
export const SignatureType = ['http', 'event', 'cloudevent'] as const;

/**
* Union type of all valid function SignatureType values.
*/
export type SignatureType = typeof SignatureType[number];

/**
* Type guard to test if a provided value is valid SignatureType
* @param x the value to test
* @returns true if the provided value is a valid SignatureType
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const isValidSignatureType = (x: any): x is SignatureType => {
return SignatureType.includes(x);
};
3 changes: 1 addition & 2 deletions test/integration/cloudevent.ts
Expand Up @@ -16,7 +16,6 @@ import * as assert from 'assert';
import * as functions from '../../src/functions';
import * as sinon from 'sinon';
import {getServer} from '../../src/server';
import {SignatureType} from '../../src/types';
import * as supertest from 'supertest';

const TEST_CLOUD_EVENT = {
Expand Down Expand Up @@ -224,7 +223,7 @@ describe('CloudEvent Function', () => {
let receivedCloudEvent: functions.CloudEventsContext | null = null;
const server = getServer((cloudevent: functions.CloudEventsContext) => {
receivedCloudEvent = cloudevent as functions.CloudEventsContext;
}, SignatureType.CLOUDEVENT);
}, 'cloudevent');
await supertest(server)
.post('/')
.set(test.headers)
Expand Down
3 changes: 1 addition & 2 deletions test/integration/http.ts
Expand Up @@ -15,7 +15,6 @@
import * as assert from 'assert';
import * as express from 'express';
import {getServer} from '../../src/server';
import {SignatureType} from '../../src/types';
import * as supertest from 'supertest';

describe('HTTP Function', () => {
Expand Down Expand Up @@ -73,7 +72,7 @@ describe('HTTP Function', () => {
query: req.query.param,
});
},
SignatureType.HTTP
'http'
);
const st = supertest(server);
await (test.httpVerb === 'GET'
Expand Down
3 changes: 1 addition & 2 deletions test/integration/legacy_event.ts
Expand Up @@ -15,7 +15,6 @@
import * as assert from 'assert';
import * as functions from '../../src/functions';
import {getServer} from '../../src/server';
import {SignatureType} from '../../src/types';
import * as supertest from 'supertest';

const TEST_CLOUD_EVENT = {
Expand Down Expand Up @@ -175,7 +174,7 @@ describe('Event Function', () => {
const server = getServer((data: {}, context: functions.Context) => {
receivedData = data;
receivedContext = context as functions.CloudFunctionsContext;
}, SignatureType.EVENT);
}, 'event');
const requestHeaders = {
'Content-Type': 'application/json',
...test.headers,
Expand Down

0 comments on commit 9cf46ed

Please sign in to comment.