refactor: simplify parser logic and duplicated code (#269)

Signed-off-by: Grant Timmerman <timmerman+devrel@google.com>
This commit is contained in:
Grant Timmerman 2020-07-24 12:08:52 -07:00 committed by GitHub
parent 9ae32c76cb
commit a6124cc350
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 80 additions and 102 deletions

79
src/parsers.ts Normal file
View File

@ -0,0 +1,79 @@
import CONSTANTS from "./constants";
import { isString, isDefinedOrThrow, isStringOrObjectOrThrow, ValidationError } from "./event/validation";
export abstract class Parser {
abstract parse(payload: Record<string, unknown> | string): unknown;
}
export class JSONParser implements Parser {
decorator?: Base64Parser;
constructor(decorator?: Base64Parser) {
this.decorator = decorator;
}
/**
* Parses the payload with an optional decorator
* @param {object|string} payload the JSON payload
* @return {object} the parsed JSON payload.
*/
parse(payload: Record<string, unknown> | string): string {
if (this.decorator) {
payload = this.decorator.parse(payload);
}
isDefinedOrThrow(payload, new ValidationError("null or undefined payload"));
isStringOrObjectOrThrow(payload, new ValidationError("invalid payload type, allowed are: string or object"));
const parseJSON = (v: Record<string, unknown> | string): string => (isString(v) ? JSON.parse(v as string) : v);
return parseJSON(payload);
}
}
export class PassThroughParser extends Parser {
parse(payload: unknown): unknown {
return payload;
}
}
const jsonParser = new JSONParser();
export const parserByContentType: { [key: string]: Parser } = {
[CONSTANTS.MIME_JSON]: jsonParser,
[CONSTANTS.MIME_CE_JSON]: jsonParser,
[CONSTANTS.DEFAULT_CONTENT_TYPE]: jsonParser,
[CONSTANTS.DEFAULT_CE_CONTENT_TYPE]: jsonParser,
[CONSTANTS.MIME_OCTET_STREAM]: new PassThroughParser(),
};
export class Base64Parser implements Parser {
decorator?: Parser;
constructor(decorator?: Parser) {
this.decorator = decorator;
}
parse(payload: Record<string, unknown> | string): string {
let payloadToParse = payload;
if (this.decorator) {
payloadToParse = this.decorator.parse(payload) as string;
}
return Buffer.from(payloadToParse as string, "base64").toString();
}
}
export interface MappedParser {
name: string;
parser: Parser;
}
export class DateParser extends Parser {
parse(payload: string): Date {
return new Date(Date.parse(payload));
}
}
export const parserByEncoding: { [key: string]: { [key: string]: Parser } } = {
base64: {
[CONSTANTS.MIME_CE_JSON]: new JSONParser(new Base64Parser()),
[CONSTANTS.MIME_OCTET_STREAM]: new PassThroughParser(),
},
};

View File

@ -1,18 +0,0 @@
import { Parser } from "./parser";
export class Base64Parser implements Parser {
decorator?: Parser;
constructor(decorator?: Parser) {
this.decorator = decorator;
}
parse(payload: Record<string, unknown> | string): string {
let payloadToParse = payload;
if (this.decorator) {
payloadToParse = this.decorator.parse(payload) as string;
}
return Buffer.from(payloadToParse as string, "base64").toString();
}
}

View File

@ -1,7 +0,0 @@
import { Parser } from "./parser";
export class DateParser extends Parser {
parse(payload: string): Date {
return new Date(Date.parse(payload));
}
}

View File

@ -1,30 +0,0 @@
import { Parser } from "./parser";
import { JSONParser } from "./json";
import { PassThroughParser } from "./pass_through";
import { Base64Parser } from "./base64";
import CONSTANTS from "../constants";
export * from "./parser";
export * from "./base64";
export * from "./json";
export * from "./date";
export * from "./mapped";
export * from "./pass_through";
const jsonParser = new JSONParser();
const passThroughParser = new PassThroughParser();
export const parserByContentType: { [key: string]: Parser } = {
[CONSTANTS.MIME_JSON]: jsonParser,
[CONSTANTS.MIME_CE_JSON]: jsonParser,
[CONSTANTS.DEFAULT_CONTENT_TYPE]: jsonParser,
[CONSTANTS.DEFAULT_CE_CONTENT_TYPE]: jsonParser,
[CONSTANTS.MIME_OCTET_STREAM]: passThroughParser,
};
export const parserByEncoding: { [key: string]: { [key: string]: Parser } } = {
base64: {
[CONSTANTS.MIME_CE_JSON]: new JSONParser(new Base64Parser()),
[CONSTANTS.MIME_OCTET_STREAM]: new PassThroughParser(),
},
};

View File

@ -1,30 +0,0 @@
import { Base64Parser } from "./base64";
import { isString, isDefinedOrThrow, isStringOrObjectOrThrow, ValidationError } from "../event/validation";
import { Parser } from "./parser";
const invalidPayloadTypeError = new ValidationError("invalid payload type, allowed are: string or object");
const nullOrUndefinedPayload = new ValidationError("null or undefined payload");
const parseJSON = (v: Record<string, unknown> | string): string => (isString(v) ? JSON.parse(v as string) : v);
export class JSONParser implements Parser {
decorator?: Base64Parser;
constructor(decorator?: Base64Parser) {
this.decorator = decorator;
}
/**
* Parses the payload with an optional decorator
* @param {object|string} payload the JSON payload
* @return {object} the parsed JSON payload.
*/
parse(payload: Record<string, unknown> | string): string {
if (this.decorator) {
payload = this.decorator.parse(payload);
}
isDefinedOrThrow(payload, nullOrUndefinedPayload);
isStringOrObjectOrThrow(payload, invalidPayloadTypeError);
return parseJSON(payload);
}
}

View File

@ -1,6 +0,0 @@
import { Parser } from "./parser";
export interface MappedParser {
name: string;
parser: Parser;
}

View File

@ -1,3 +0,0 @@
export abstract class Parser {
abstract parse(payload: Record<string, unknown> | string): unknown;
}

View File

@ -1,7 +0,0 @@
import { Parser } from "./parser";
export class PassThroughParser extends Parser {
parse(payload: unknown): unknown {
return payload;
}
}

View File

@ -1,7 +1,7 @@
import "mocha";
import { expect } from "chai";
import { JSONParser as Parser } from "../../src/parsers/";
import { JSONParser as Parser } from "../../src/parsers";
import { ValidationError } from "../../src/";
describe("JSON Event Format Parser", () => {