Skip to content

Latest commit

 

History

History
236 lines (166 loc) · 8.07 KB

File metadata and controls

236 lines (166 loc) · 8.07 KB

Migrating from v1 to v2

This doc summarises the breaking changes introduced in v2 and what you need to do to update your projects to work with it.

The new version of dependency injection does not work if code is minified. See Notes in the main readme for how to turn this off in webpack.

Configuration

v1 required several consts with shouty names. In v2 these are replaced with a single config object with camel-case keys. You pass this to the new configure method to get a configured instance of LambdaWrapper.

Instead of this:

// src/config/Configuration.js
import { DEFINITIONS as CORE_DEFINITIONS } from '@comicrelief/lambda-wrapper';

import { MyService } from '@/src/services/MyService';

export const DEFINITIONS = {
  ...CORE_DEFINITIONS,
  MY_SERVICE: 'MY_SERVICE',
};

export const DEPENDENCIES = {
  [DEFINITIONS.MY_SERVICE]: MyService,
};

export const QUEUE_DEFINITIONS = {
  MY_QUEUE: 'MY_QUEUE',
};

export const QUEUES = {
  [QUEUE_DEFINITIONS.MY_QUEUE]: process.env.SQS_MY_QUEUE,
};

export default {
  DEFINITIONS,
  DEPENDENCIES,
  QUEUE_DEFINITIONS,
  QUEUES,
};

do this:

// src/config/LambdaWrapper.ts
import lw from '@comicrelief/lambda-wrapper';

import { MyService } from '@/src/services/MyService';

export const lambdaWrapper = lw.configure({
  dependencies: {
    MyService,
  },
  sqs: {
    queues: {
      myQueue: process.env.SQS_MY_QUEUE as string,
    },
  },
});

Wrapping a function

Rather than passing in a config object everywhere you use Lambda Wrapper, you now simply use the configured instance.

v2 also drops support for callback-style async. Use promises instead.

Finally, there is no longer a request parameter provided to your wrapped function. You can get this from di if you need it.

Instead of this:

import { LambdaWrapper } from '@comicrelief/lambda-wrapper';

import { CONFIGURATION, DEFINITIONS } from '@/src/config/Configuration';

export default LambdaWrapper(CONFIGURATION, (di, request, done) => {
  // ...
  done(null, response);
});

do this:

import lambdaWrapper from '@/src/config/LambdaWrapper';

export default lambdaWrapper.wrap(async (di) => {
  const request = di.get(RequestService);
  // ...
  return response;
});

If your project doesn't add any additional services to dependency injection, you can also now use lambdaWrapper straight out of the box:

import lambdaWrapper from '@comicrelief/lambda-wrapper';

export default lambdaWrapper.wrap(async (di) => {
  // ...
});

Dependency injection

As you'll have seen in the above examples, dependencies are no longer identified by a DEFINITIONS string. The get method now takes the dependency class directly.

Instead of this:

import { LambdaWrapper } from '@comicrelief/lambda-wrapper';

import { CONFIGURATION, DEFINITIONS } from '@/src/config/Configuration';

export default LambdaWrapper(CONFIGURATION, (di, request, done) => {
  const logger = di.get(DEFINITIONS.LOGGER);
  const myService = di.get(DEFINITIONS.MY_SERVICE);
  // ...
});

do this:

import { LoggerService, RequestService } from '@comicrelief/lambda-wrapper';

import lambdaWrapper from '@/src/config/LambdaWrapper';
import { MyService } from '@/src/services/MyService';

export default lambdaWrapper.wrap(async (di) => {
  const logger = di.get(LoggerService);
  const request = di.get(RequestService);
  const myService = di.get(MyService);
  // ...
});

get will also always throw an error when used in a constructor to avoid surprises where other dependencies may be undefined. Instead of storing references to dependencies in class members, get them just before use.

A further breaking change in v2 is that all dependencies must extend DependencyAwareClass. This is enforced at the type level and also at runtime, for those using plain JavaScript. Remember to add a call to super if you are overriding the constructor.

The definitions property has been removed.

The getEvent, getContext and getConfiguration methods have been deprecated and will be removed in a future major release. Use the event, context and config properties directly.

Services

There are some small breaking changes to the built-in services. We've tried to minimise disruption here, so most of these are unlikely to affect real-world applications, but are included in the 2.0.0 release as a precaution.

RequestService

Header names returned by getAllHeaders are now lowercased. This is consistent with many other libraries (e.g. http, axios) and makes it easier to work with HTTP headers. In some cases it may be easier to change existing code to use getHeader, which has provided case-insensitive access to headers since v1.2.0.

SQSService

In v1, the default behaviour of publish was to catch any error thrown while sending the message to SQS. In v2, this has been changed and it will now throw an error by default. In most scenarios this is the more intuitive mode, as it ensures that the caller is made aware of any SQS-related failure. To maintain the old behaviour, you can pass "catch" in the failureMode parameter.

// v1
sqs.publish(queue, message);
// v2 equivalent
sqs.publish(queue, message, undefined, 'catch');

We encourage use of undefined instead of null if you are not using the messageGroupId parameter. This keeps the type simple. null will continue to be accepted but is deprecated as of v2, and it will be dropped from the type in v3.

Models

The Model base class has been removed. It's hard to make it type-safe (it tries to dynamically call setter methods) and we do modelling and validation differently now, using our data-models repo which is based around Yup.

The MarketingPreference model is removed, as this is application-specific and again is replaced by our data-models repo.

The StatusModel model has been replaced by a simple object type. See the StatusModel section below.

Other models (ResponseModel, SQSMessageModel) are largely unaffected except that they no longer inherit from a common Model class.

StatusModel

Service statuses are now simple objects with the ServiceStatus type. The STATUS_TYPES object has also been removed in favour of a string union type.

Instead of this:

import { StatusModel, STATUS_TYPES } from '@comicrelief/lambda-wrapper';

async function checkStatus() {
  const result = new StatusModel('My service', STATUS_TYPES.OK);
  try {
    await testMyService(); // some function that throws if there's a failure
  } catch (error) {
    result.setStatus(STATUS_TYPES.APPLICATION_FAILURE);
  }
  return result;
}

do this:

import { ServiceStatus, StatusValue } from '@comicrelief/lambda-wrapper';

async function checkStatus(): Promise<ServiceStatus> {
  let status: StatusValue;
  try {
    await testMyService(); // some function that throws if there's a failure
    status = 'OK';
  } catch (error) {
    status = 'APPLICATION_FAILURE';
  }
  return {
    service: 'My service',
    status,
  };
}

Note that we can keep status unset initially, and TypeScript will complain if you forget to set it before checkStatus returns.

SQSMessageModel

The model constructor will now validate that message has all fields required of a received SQS message. This should not break existing applications that are using this model correctly, but is included in the 2.0.0 release as a precaution.

The class name has also changed from Message to SQSMessageModel in order to be consistent with the name exported from the package index. Again, this is not expected to break anything, and does not affect imports because the class is a default export.