Skip to content

Commit

Permalink
Store construct classes in AwsProvider and make them static
Browse files Browse the repository at this point in the history
  • Loading branch information
mnapoli committed Jun 21, 2021
1 parent baaedbc commit 6364ad5
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 34 deletions.
65 changes: 46 additions & 19 deletions src/classes/AwsProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,34 @@ import { awsRequest } from "./aws";
import { ConstructInterface } from ".";
import { StaticConstructInterface } from "./Construct";
import ServerlessError from "../utils/error";
import { Storage } from "../constructs/Storage";
import { Queue } from "../constructs/Queue";
import { Webhook } from "../constructs/Webhook";
import { StaticWebsite } from "../constructs/StaticWebsite";

export class AwsProvider {
private readonly constructClasses: StaticConstructInterface[] = [];
private static readonly constructClasses: Record<string, StaticConstructInterface> = {};

static registerConstructs(...constructClasses: StaticConstructInterface[]): void {
for (const constructClass of constructClasses) {
if (constructClass.type in this.constructClasses) {
throw new ServerlessError(
`The construct type '${constructClass.type}' was registered twice`,
"LIFT_CONSTRUCT_TYPE_CONFLICT"
);
}
this.constructClasses[constructClass.type] = constructClass;
}
}

static getConstructClass(type: string): StaticConstructInterface | undefined {
return this.constructClasses[type];
}

static getAllConstructClasses(): StaticConstructInterface[] {
return Object.values(this.constructClasses);
}

private readonly app: App;
public readonly stack: Stack;
public readonly region: string;
Expand All @@ -27,26 +52,18 @@ export class AwsProvider {
this.region = serverless.getProvider("aws").getRegion();
}

registerConstructs(...constructClasses: StaticConstructInterface[]): void {
this.constructClasses.push(...constructClasses);
}

getAllConstructClasses(): StaticConstructInterface[] {
return this.constructClasses;
}

create(type: string, id: string): ConstructInterface {
const configuration = get(this.serverless.configurationInput.constructs, id, {});
for (const Construct of this.constructClasses) {
if (Construct.type === type) {
return Construct.create(this, id, configuration);
}
const Construct = AwsProvider.getConstructClass(type);
if (Construct === undefined) {
throw new ServerlessError(
`The construct '${id}' has an unknown type '${type}'\n` +
"Find all construct types available here: https://github.com/getlift/lift#constructs",
"LIFT_UNKNOWN_CONSTRUCT_TYPE"
);
}
throw new ServerlessError(
`The construct '${id}' has an unknown type '${type}'\n` +
"Find all construct types available here: https://github.com/getlift/lift#constructs",
"LIFT_UNKNOWN_CONSTRUCT_TYPE"
);
const configuration = get(this.serverless.configurationInput.constructs, id, {});

return Construct.create(this, id, configuration);
}

addFunction(functionName: string, functionConfig: unknown): void {
Expand Down Expand Up @@ -82,3 +99,13 @@ export class AwsProvider {
});
}
}

/**
* This is representative of a possible public API to register constructs. How it would work:
* - 3rd party developers create a custom construct
* - they also create a plugin that calls:
* AwsProvider.registerConstructs(Foo, Bar);
* If they use TypeScript, `registerConstructs()` will validate that the construct class
* implements both static fields (type, schema, create(), …) and non-static fields (outputs(), references(), …).
*/
AwsProvider.registerConstructs(Storage, Queue, Webhook, StaticWebsite);
18 changes: 3 additions & 15 deletions src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@ import type {
import { AwsProvider, ConstructInterface } from "./classes";
import { log } from "./utils/logger";
import { StaticConstructInterface } from "./classes/Construct";
import { Storage } from "./constructs/Storage";
import { Queue } from "./constructs/Queue";
import { Webhook } from "./constructs/Webhook";
import { StaticWebsite } from "./constructs/StaticWebsite";
import ServerlessError from "./utils/error";

const CONSTRUCT_ID_PATTERN = "^[a-zA-Z0-9-_]+$";
Expand Down Expand Up @@ -45,6 +41,7 @@ const CONSTRUCTS_DEFINITION = {
class LiftPlugin {
private readonly constructs: Record<string, ConstructInterface> = {};
private readonly serverless: Serverless;
private readonly providerClasses: typeof AwsProvider[] = [];
private readonly providers: AwsProvider[] = [];
private readonly schema = CONSTRUCTS_DEFINITION;
public readonly hooks: Record<string, Hook>;
Expand Down Expand Up @@ -86,24 +83,14 @@ class LiftPlugin {
};

this.registerProviders();
/**
* This is representative of a possible public API to register constructs. How it would work:
* - 3rd party developers create a custom construct
* - they also create a plugin that calls:
* const awsProvider = serverless.lift.getProvider("aws");
* awsProvider.registerConstructs(Foo, Bar);
* If they use TypeScript, `registerConstructs()` will validate that the construct class
* implements both static fields (type, schema, create(), …) and non-static fields (outputs(), references(), …).
*/
this.providers[0].registerConstructs(Storage, Queue, Webhook, StaticWebsite);
this.registerConstructsSchema();
this.registerConfigSchema();
this.loadConstructs();
this.registerCommands();
}

private getAllConstructClasses(): StaticConstructInterface[] {
return flatten(this.providers.map((provider) => provider.getAllConstructClasses()));
return flatten(this.providerClasses.map((providerClass) => providerClass.getAllConstructClasses()));
}

private registerConstructsSchema() {
Expand All @@ -126,6 +113,7 @@ class LiftPlugin {
}

private registerProviders() {
this.providerClasses.push(AwsProvider);
this.providers.push(new AwsProvider(this.serverless));
}

Expand Down

0 comments on commit 6364ad5

Please sign in to comment.