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

A RestApi and RequestAuthorizer in separate projects throws an error #7377

Closed
pofallon opened this issue Apr 16, 2020 · 8 comments · Fixed by #7596 or bifravst/cloudformation-cleaner#13
Assignees
Labels
@aws-cdk/aws-apigateway Related to Amazon API Gateway bug This issue is a bug. p1

Comments

@pofallon
Copy link
Contributor

I have a project (see reproducible sample below) where an API Gateway RestApi is defined in a CDK stack and passed as a property to a CDK library. In this library a RequestAuthorizer is defined and attached to a method on the RestApi. This process works when the RestApi and RequestAuthorizer are two TypeScript modules in the same project. However, when they are in separate projects, cdk synth fails with an Authorizer (...) must be attached to a RestApi. error.

Reproduction Steps

This example project has code and steps to reproduce the error. In short:

  • Clone the example and add-on repos
  • npm install, build and link the two projects
  • Run cdk synth and see the error

Error Log

/Users/paul/Documents/GitHub/api-authorizer-addon/node_modules/@aws-cdk/aws-apigateway/lib/authorizers/lambda.ts:123
          throw new Error(`Authorizer (${this.node.path}) must be attached to a RestApi`);
                ^
Error: Resolution error: Resolution error: Resolution error: Authorizer (ApiAuthorizerExampleStack/AddOn/Authorizer) must be attached to a RestApi.

Environment

  • CLI Version : 1.32.2
  • Framework Version: 1.32.2
  • OS : MacOS Catalina
  • **Language : TypeScript **

Other

Stack trace:

/Users/paul/Documents/GitHub/api-authorizer-addon/node_modules/@aws-cdk/aws-apigateway/lib/authorizers/lambda.ts:123
          throw new Error(`Authorizer (${this.node.path}) must be attached to a RestApi`);
                ^
Error: Resolution error: Resolution error: Resolution error: Authorizer (ApiAuthorizerExampleStack/AddOn/Authorizer) must be attached to a RestApi.
Object creation stack:
  at new LazyBase (/Users/paul/Documents/GitHub/api-authorizer-addon/node_modules/@aws-cdk/core/lib/lazy.ts:126:26)
  at new LazyString (/Users/paul/Documents/GitHub/api-authorizer-addon/node_modules/@aws-cdk/core/lib/lazy.ts:147:5)
  at Function.stringValue (/Users/paul/Documents/GitHub/api-authorizer-addon/node_modules/@aws-cdk/core/lib/lazy.ts:103:27)
  at RequestAuthorizer.lazyRestApiId (/Users/paul/Documents/GitHub/api-authorizer-addon/node_modules/@aws-cdk/aws-apigateway/lib/authorizers/lambda.ts:120:17)
  at new RequestAuthorizer (/Users/paul/Documents/GitHub/api-authorizer-addon/node_modules/@aws-cdk/aws-apigateway/lib/authorizers/lambda.ts:230:28)
  at new AddOn (/Users/paul/Documents/GitHub/api-authorizer-addon/lib/index.ts:23:18)
  at new ApiAuthorizerExampleStack (/Users/paul/Documents/GitHub/api-authorizer-example/lib/api-authorizer-example-stack.ts:10:19)
  at Object.<anonymous> (/Users/paul/Documents/GitHub/api-authorizer-example/bin/api-authorizer-example.ts:7:1)
  at Module._compile (internal/modules/cjs/loader.js:1123:30)
  at Module.m._compile (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/ts-node/src/index.ts:836:23)
  at Module._extensions..js (internal/modules/cjs/loader.js:1143:10)
  at Object.require.extensions.<computed> [as .ts] (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/ts-node/src/index.ts:839:12)
  at Module.load (internal/modules/cjs/loader.js:972:32)
  at Function.Module._load (internal/modules/cjs/loader.js:872:14)
  at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
  at main (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/ts-node/src/bin.ts:226:14)
  at Object.<anonymous> (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/ts-node/src/bin.ts:485:3)
  at Module._compile (internal/modules/cjs/loader.js:1123:30)
  at Object.Module._extensions..js (internal/modules/cjs/loader.js:1143:10)
  at Module.load (internal/modules/cjs/loader.js:972:32)
  at Function.Module._load (internal/modules/cjs/loader.js:872:14)
  at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
  at /usr/local/lib/node_modules/npm/node_modules/libnpx/index.js:268:14.
Object creation stack:
  at new Intrinsic (/Users/paul/Documents/GitHub/api-authorizer-addon/node_modules/@aws-cdk/core/lib/private/intrinsic.ts:28:26)
  at new PostResolveToken (/Users/paul/Documents/GitHub/api-authorizer-addon/node_modules/@aws-cdk/core/lib/util.ts:83:5)
  at Object.ignoreEmpty (/Users/paul/Documents/GitHub/api-authorizer-addon/node_modules/@aws-cdk/core/lib/util.ts:38:10)
  at CfnPermission._toCloudFormation (/Users/paul/Documents/GitHub/api-authorizer-addon/node_modules/@aws-cdk/core/lib/cfn-resource.ts:270:25)
  at /Users/paul/Documents/GitHub/api-authorizer-example/node_modules/@aws-cdk/core/lib/stack.ts:1090:58
  at Object.findTokens (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/@aws-cdk/core/lib/private/resolve.ts:173:11)
  at ApiAuthorizerExampleStack.findTokens (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/@aws-cdk/core/lib/stack.ts:1090:24)
  at ApiAuthorizerExampleStack.prepare (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/@aws-cdk/core/lib/stack.ts:747:25)
  at ApiAuthorizerExampleStack.onPrepare (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/@aws-cdk/core/lib/construct-compat.ts:105:10)
  at Node.prepare (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/constructs/lib/construct.ts:440:28)
  at Node.synthesize (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/constructs/lib/construct.ts:399:10)
  at Function.synth (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/@aws-cdk/core/lib/construct-compat.ts:225:22)
  at App.synth (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/@aws-cdk/core/lib/app.ts:141:36)
  at process.<anonymous> (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/@aws-cdk/core/lib/app.ts:120:45)
  at Object.onceWrapper (events.js:422:26)
  at process.emit (events.js:315:20)
  at process.EventEmitter.emit (domain.js:485:12)
  at process.emit (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/source-map-support/source-map-support.js:485:21)
  at process.topLevelDomainCallback (domain.js:137:15).
Object creation stack:
  at new Intrinsic (/Users/paul/Documents/GitHub/api-authorizer-addon/node_modules/@aws-cdk/core/lib/private/intrinsic.ts:28:26)
  at new PostResolveToken (/Users/paul/Documents/GitHub/api-authorizer-addon/node_modules/@aws-cdk/core/lib/util.ts:83:5)
  at CfnPermission._toCloudFormation (/Users/paul/Documents/GitHub/api-authorizer-addon/node_modules/@aws-cdk/core/lib/cfn-resource.ts:268:29)
  at /Users/paul/Documents/GitHub/api-authorizer-example/node_modules/@aws-cdk/core/lib/stack.ts:1090:58
  at Object.findTokens (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/@aws-cdk/core/lib/private/resolve.ts:173:11)
  at ApiAuthorizerExampleStack.findTokens (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/@aws-cdk/core/lib/stack.ts:1090:24)
  at ApiAuthorizerExampleStack.prepare (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/@aws-cdk/core/lib/stack.ts:747:25)
  at ApiAuthorizerExampleStack.onPrepare (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/@aws-cdk/core/lib/construct-compat.ts:105:10)
  at Node.prepare (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/constructs/lib/construct.ts:440:28)
  at Node.synthesize (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/constructs/lib/construct.ts:399:10)
  at Function.synth (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/@aws-cdk/core/lib/construct-compat.ts:225:22)
  at App.synth (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/@aws-cdk/core/lib/app.ts:141:36)
  at process.<anonymous> (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/@aws-cdk/core/lib/app.ts:120:45)
  at Object.onceWrapper (events.js:422:26)
  at process.emit (events.js:315:20)
  at process.EventEmitter.emit (domain.js:485:12)
  at process.emit (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/source-map-support/source-map-support.js:485:21)
  at process.topLevelDomainCallback (domain.js:137:15)
    at Object.produce (/Users/paul/Documents/GitHub/api-authorizer-addon/node_modules/@aws-cdk/aws-apigateway/lib/authorizers/lambda.ts:123:17)
    at LazyString.resolve (/Users/paul/Documents/GitHub/api-authorizer-addon/node_modules/@aws-cdk/core/lib/lazy.ts:151:26)
    at RememberingTokenResolver.resolveToken (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/@aws-cdk/core/lib/resolvable.ts:134:24)
    at RememberingTokenResolver.resolveToken (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/@aws-cdk/core/lib/private/resolve.ts:186:18)
    at resolve (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/@aws-cdk/core/lib/private/resolve.ts:133:29)
    at Object.resolve [as mapToken] (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/@aws-cdk/core/lib/private/resolve.ts:49:32)
    at TokenizedStringFragments.mapTokens (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/@aws-cdk/core/lib/string-fragments.ts:71:33)
    at RememberingTokenResolver.resolveString (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/@aws-cdk/core/lib/resolvable.ts:155:22)
    at RememberingTokenResolver.resolveString (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/@aws-cdk/core/lib/private/resolve.ts:190:23)
    at resolve (/Users/paul/Documents/GitHub/api-authorizer-example/node_modules/@aws-cdk/core/lib/private/resolve.ts:91:31)
Subprocess exited with error 1

This is 🐛 Bug Report

@pofallon pofallon added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Apr 16, 2020
@SomayaB SomayaB added the @aws-cdk/aws-apigateway Related to Amazon API Gateway label Apr 16, 2020
@pofallon
Copy link
Contributor Author

pofallon commented Apr 17, 2020

It looks like this line in lib/method.ts that calls authorizer._attachToApi() isn't being reached.

If I munge the resulting javascript in my node_modules directory, I can get cdk synth to succeed.

// if (authorizer instanceof authorizer_1.Authorizer) {
if (authorizer instanceof Object && typeof authorizer._attachToApi === 'function') {
    authorizer._attachToApi(this.restApi);
}

@nija-at
Copy link
Contributor

nija-at commented Apr 22, 2020

@pofallon - can you check if both of your libraries are using the same version of the CDK packages during typescript compilation of each of them? package-lock.json or yarn.lock should tell you this.

The instanceof match will fail if the two libraries are using different versions of the @aws-cdk/aws-apigateway module.

@nija-at nija-at added response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. and removed needs-triage This issue or PR still needs to be triaged. labels Apr 22, 2020
@pofallon
Copy link
Contributor Author

Hello! Thanks for following up. I double-checked and confirmed they're running the same version:

$ grep "@aws-cdk/aws-apigateway" api-authorizer*/package-lock.json 
api-authorizer-addon/package-lock.json:    "@aws-cdk/aws-apigateway": {
api-authorizer-addon/package-lock.json:      "resolved": "https://registry.npmjs.org/@aws-cdk/aws-apigateway/-/aws-apigateway-1.34.0.tgz",
api-authorizer-example/package-lock.json:    "@aws-cdk/aws-apigateway": {
api-authorizer-example/package-lock.json:      "resolved": "https://registry.npmjs.org/@aws-cdk/aws-apigateway/-/aws-apigateway-1.34.0.tgz",

(I've updated the libraries locally since filing the ticket, to see if it was still a problem -- it is.)

@nija-at nija-at removed the response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. label Apr 24, 2020
@RomainMuller
Copy link
Contributor

The instanceof match will fail if the two libraries are using different versions of the @aws-cdk/aws-apigateway module.

Or two different instances of the same version... If the library is installed at two different points in the runtime closure (aka it is present more than once in the output of npm ls), it will result in two different instances of the constructor (and instanceof is akin to doing === on the constructors).

@nija-at
Copy link
Contributor

nija-at commented Apr 24, 2020

I am able to reproduce this issue.

@RomainMuller - unfortunately that doesn't seem to be the issue. Exactly one instance of the module is installed in node tree.

⇒  npm ls | grep apigateway
├─┬ @aws-cdk/aws-apigateway@1.32.2
⇒  tree node_modules | grep aws-apigateway
│   ├── aws-apigateway

@RomainMuller
Copy link
Contributor

RomainMuller commented Apr 24, 2020

The setting up of this project involves using npm link ., which actually means the add-on package will use its own copy of @aws-cdk/aws-apigateway, while the example package will use a different one.

You can easily verify this by issuing console.log(require.resolve('@aws-cdk/aws-apigateway')); from somewhere in each package... You'll see they both log different paths.

This is a quirk of how npm link works it's magic (I reckon it would work correctly if you were to npm install the add-on package instead of linking it, but it'd make your development iteration cycle that much more painful).

I guess this is one more reason to avoid using instanceof unless you are able to guarantee that the package (instance) where the instanceof is performed is also the one that created the instance (which more often than none isn't something you can assume).

@nija-at
Copy link
Contributor

nija-at commented Apr 24, 2020

Thanks @RomainMuller for the lead.

Confirmed that the problem was that there were two instances of the @aws-cdk/aws-apigateway packages under node_modules - one being used for creating the RequestAuthorizer class and the other being used while comparing this with Authorizer.

@nija-at nija-at added the p1 label Apr 24, 2020
nija-at pushed a commit that referenced this issue Apr 24, 2020
When an Authorizer is defined in one node project, imported into another
where it is wired up with a RestApi, synthesis throws the error
'Authorizer must be attached to a RestApi'.

The root cause of this error is the `instanceof` check. If a user has
their project set up in a way that more than one instance of the
`@aws-cdk/aws-apigateway` module is present in their `node_modules/`
folder, even if they are of the exact same version, type checking is
bound to fail.

Switching instead to a symbol property based comparison, so that type
information survives across installations of the module.

fixes #7377
nija-at pushed a commit that referenced this issue Apr 24, 2020
When an Authorizer is defined in one node project, imported into another
where it is wired up with a RestApi, synthesis throws the error
'Authorizer must be attached to a RestApi'.

The root cause of this error is the `instanceof` check. If a user has
their project set up in a way that more than one instance of the
`@aws-cdk/aws-apigateway` module is present in their `node_modules/`
folder, even if they are of the exact same version, type checking is
bound to fail.

Switching instead to a symbol property based comparison, so that type
information survives across installations of the module.

fixes #7377
@mergify mergify bot closed this as completed in #7596 Apr 24, 2020
mergify bot pushed a commit that referenced this issue Apr 24, 2020
#7596)

When an Authorizer is defined in one node project, imported into another
where it is wired up with a RestApi, synthesis throws the error
'Authorizer must be attached to a RestApi'.

The root cause of this error is the `instanceof` check. If a user has
their project set up in a way that more than one instance of the
`@aws-cdk/aws-apigateway` module is present in their `node_modules/`
folder, even if they are of the exact same version, type checking is
bound to fail.

Switching instead to a symbol property based comparison, so that type
information survives across installations of the module.

fixes #7377
@pofallon
Copy link
Contributor Author

Thanks so much for the fix - I really appreciate it!

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