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

Dynamic table resolving from serverless.yml after upgrade to Serverless 3 #124

Open
jenssimon opened this issue Feb 4, 2022 · 4 comments

Comments

@jenssimon
Copy link

I try to upgrade a project to Serverless 3 which uses this code from readme in jest-dynamodb-config.js.

module.exports = async () => {
  const serverless = new (require('serverless'))();
  // If using monorepo where DynamoDB serverless.yml is in another directory
  // const serverless = new (require('serverless'))({ servicePath: '../../../core/data' });

  await serverless.init();
  const service = await serverless.variables.populateService();
  const resources = service.resources.filter(r => Object.keys(r).includes('Resources'))[0];

  const tables = Object.keys(resources)
    .map(name => resources[name])
    .filter(r => r.Type === 'AWS::DynamoDB::Table')
    .map(r => r.Properties);

  return {
    tables,
    port: 8000
  };
};

Works perfect for Serverless 2. But with Serverless 3 I get this error:

TypeError: Jest: Got error running globalSetup - /path/to/project/node_modules/@shelf/jest-dynamodb/setup.js, reason: undefined is not an array
    at module.exports (/path/to/project/node_modules/type/lib/resolve-exception.js:12:14)
    at module.exports (/path/to/project/node_modules/type/array/ensure.js:15:25)
    at new Serverless (/path/to/project/node_modules/serverless/lib/serverless.js:84:22)
    at module.exports (/path/to/project/jest-dynamodb-config.js:4:24)
    at module.exports (/path/to/project/node_modules/@shelf/jest-dynamodb/setup.js:21:44)
    at /path/to/project/node_modules/@jest/core/build/runGlobalHook.js:125:19
    at ScriptTransformer.requireAndTranspileModule (/path/to/project/node_modules/@jest/transform/build/ScriptTransformer.js:903:24)
    at async _default (/path/to/project/node_modules/@jest/core/build/runGlobalHook.js:116:9)
    at async runJest (/path/to/project/node_modules/@jest/core/build/runJest.js:369:5)
    at async _run10000 (/path/to/project/node_modules/@jest/core/build/cli/index.js:320:7)
    at async runCLI (/path/to/project/node_modules/@jest/core/build/cli/index.js:173:3)
    at async Object.run (/path/to/project/node_modules/jest-cli/build/cli/index.js:155:37)

Is there an updated version of that code that works with Serverless 3?

Thanks!

@robsonmafra
Copy link

Same here! I couldn't find a solution or workaround.

@robsonmafra
Copy link

robsonmafra commented Feb 26, 2022

@jenssimon , did you find a solution? I found here the details about the Serverless initialisation after version 3:

Serverless constructor was refactored to depend on service configuration being resolved externally and passed to its constructor with following options:
configuration - Service configuration (JSON serializable plain object)
serviceDir - Directory in which service is placed (All path references in service configuration will be resolved against this path)
configurationFilename - Name of configuration file (e.g. serverless.yml).

Starting from v3.0.0 configuration data will not be resolved internally, and if Serverless is invoked in service context, all three options will have to be provided
Reference: https://www.serverless.com/framework/docs/deprecations#serverless-constructor-service-configuration-dependency

So, I just initialised manually the required variables, but it stills not load as the serverless.variables.populateService(); was also removed. I will share here in case that helps:

const Serverless = require('serverless');
const path = require('path');
const fs = require('fs');
const yaml = require('js-yaml');

module.exports = async () => {
  const serviceDir = process.cwd();
  const configurationFilename = 'serverless.yml';
  const configuration = yaml.load(fs.readFileSync(path.resolve(serviceDir, configurationFilename), 'utf8'));
  const serverless = new Serverless({ configuration, serviceDir, configurationFilename, commands: [], options: {} });
  await serverless.init();
  const resources = serverless.service.resources.Resources;

  const tables = Object.keys(resources)
    .map((name) => resources[name])
    .filter((r) => r.Type === 'AWS::DynamoDB::Table')
    .map((r) => r.Properties);

  return {
    tables,
    port: 8000,
  };
};

@jenssimon
Copy link
Author

Thanks @robsonmafra for your input.

Haven't had time to check on this for a while. I decided to process the configuration without Serverless and take care of the variables by myself. In my case I use some hard coded values for now.

But would be nice to have a smarter generic solution (as it was for serverless < 3).

@domdomegg
Copy link
Contributor

A more general method is using output of the print command, which helps if you've used subsitutions in your yaml, or have defined it in TypeScript and want to consume it in JavaScript.

For example:

const path = require("path")
const execSync = require('child_process').execSync;

module.exports = async () => {
  const service = JSON.parse(execSync('npx serverless print --format json', { encoding: 'utf-8' }))

  const tables = Object.values(service.resources.Resources)
    .filter((resource) => resource.Type === "AWS::DynamoDB::Table")
    .map((resource) => resource.Properties)

  return {
    tables,
    port: 8000,
  }
}

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

No branches or pull requests

3 participants