Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate away from class-transfomer #100

Open
RichiCoder1 opened this issue Feb 23, 2023 · 1 comment
Open

Migrate away from class-transfomer #100

RichiCoder1 opened this issue Feb 23, 2023 · 1 comment

Comments

@RichiCoder1
Copy link

RichiCoder1 commented Feb 23, 2023

This is a rethought version of my previous ticket #68

To resummarize the issues with class-transformer:

  • The serialization breaks down in the face of unsupported CloudFormation JSON Schema Features, and increases complexity since JSON Schema features must be manually mapped to the Class Transformer equivalent.
  • It ties the implementation to TypeScript's decorators which are soon to be completely deprecated and redesigned entirely, and results in a data model that might be foreign to a lot of JavaScript developers today (outside angular and nestjs)
  • Because of the above, it prevents JavaScript from working (though I'd personally always encourage TS over JS) (Support for JavaScript #8)
  • The project isn't also super alive right now: question: is the project still alive ? typestack/class-transformer#1272 Possibly no longer as valid, it looks like the projects might be getting new maintainers.

The updated proposal proposes swapping class-transformer with three components:

  • The package camelcase-keys to handle the CloudFormation-to-JS object key camelcasing.
  • The package AJV to handle validating the incoming event properties, provide friendly(er) error messages, as well as automatic type conversion
  • The package json-schema-to-typescript to handle consuming the resource definition and outputting type information for a richer experience.

While a very large change, it would uncouple this plugin from TypeScript and an unmaintained library while hopefully simplifying the Dev UX of developing a resource in typescript.

For example, a simple handler like the template default would become (example greatly appreviated):

// handlers.ts
import { Resource, TypeConfiguration } from "./.generated/models";
import { createResource, ProgressEvent, exceptions } from '@amazon-web-services-cloudformation/cloudformation-cli-typescript-lib';

const { entrypoint, testEntrypoint } = createResource<Resource, TypeConfiguration>({
    typeName: Resource.TypeName,
	schema: Resource.Schema,
    // Type information for all the below is automatically infered
	async create({ session, properties, request, logger, typeConfiguration }) {
        // Example:
        try {
			const { apiKey } = typeConfiguration;
            const response = await fetch(`https://api.someservice.com`, {
              method: 'POST',
              headers: { 'x-api-key': apiKey },
              body: { ...properties },
			});
            const { id } = await response.json();
            properties.id = id;
            // else handle error
        } catch(err) {
            logger.log(err);
            // exceptions module lets CloudFormation know the type of failure that occurred
            throw new exceptions.InternalFailure(err.message);
            // this can also be done by returning a failed progress event
            // return ProgressEvent.failed(HandlerErrorCode.InternalFailure, err.message);
        }
        return properties;
    },
    /* more handlers.... */
    async list({ properties, typeConfiguration }) {
	   /* ...some list code... */
       // Just return a plain array of models, validate via typescript & ajv
       return [/* list of plain old javascript models */];
    },
});

export { entrypoint, testEntrypoint };

This also externalizes a lot of concerns unnecessary to the user code, infers a lot more of type information automatically, and makes developing resource types much less mentally onerous allowing developers to focus on business logic.

It would require more work with the SDK and generated code however.

@RichiCoder1
Copy link
Author

RichiCoder1 commented Mar 4, 2023

I've got a basic PoC branch moving now: https://github.com/aws-cloudformation/cloudformation-cli-typescript-plugin/compare/master...RichiCoder1:cloudformation-cli-typescript-plugin:feat/drop_decorators?expand=1

Ended up using AJV via a some superset tools called typeconv and suretype which provide schema generate and validation respectively.

Validated that this gets us validation and rich type information, and using camelcase-keys gets us the nice, typescript-y experience.

What I'm working on now is:

  • Breaking apart the progress event builder. While the CFN Cli docs recommend it, it's somewhat out of place in the usually UX of developing javascript applications. The plan is to get rid of the builder (and some of the other abstractions) and provide helpers and type validation to end users to emit properly formed progress events.
  • Simplifying session proxying, logging, and metric delivery. It uses methods not forward compatible with v3. Thinking about dropping a lot of the abstractions and just wrapping (and exposing to end users) a p-queue based queue for handling fire-and-forget calls.
  • Generally removing usage of decorators and class transformer. Ideally we're just left with POJOs and class that handle marshalling.

I might honestly end up making a completely clean fork for the plugin as it'll be a major API change from the current state of this plugin.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant