-
Notifications
You must be signed in to change notification settings - Fork 72
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add basic function implementation (#87)
- Loading branch information
1 parent
9016863
commit b5afd08
Showing
40 changed files
with
2,436 additions
and
66 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# Be very careful editing this file. It is crafted to work around [this issue](https://github.com/npm/npm/issues/4479) | ||
|
||
# First ignore everything | ||
**/* | ||
|
||
# Then add back in transpiled js and ts declaration files | ||
!lib/**/*.js | ||
!lib/**/*.d.ts | ||
|
||
# Then ignore test js and ts declaration files | ||
*.test.js | ||
*.test.d.ts | ||
|
||
# This leaves us with including only js and ts declaration files of functional code |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
## API Report File for "@aws-amplify/backend-function" | ||
|
||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). | ||
```ts | ||
|
||
import { AmplifyFunction } from '@aws-amplify/function-construct'; | ||
import { AmplifyFunctionProps } from '@aws-amplify/function-construct'; | ||
import { ConstructFactory } from '@aws-amplify/plugin-types'; | ||
import { ConstructFactoryGetInstanceProps } from '@aws-amplify/plugin-types'; | ||
|
||
// @public | ||
export class AmplifyFunctionFactory implements ConstructFactory<AmplifyFunction> { | ||
static build(props: AmplifyFunctionFactoryBuildProps): Promise<AmplifyFunctionFactory>; | ||
static fromDir(props: AmplifyFunctionFactoryFromDirProps): AmplifyFunctionFactory; | ||
getInstance({ constructContainer, }: ConstructFactoryGetInstanceProps): AmplifyFunction; | ||
} | ||
|
||
// @public (undocumented) | ||
export type AmplifyFunctionFactoryBaseProps = { | ||
name: string; | ||
}; | ||
|
||
// @public (undocumented) | ||
export type AmplifyFunctionFactoryBuildProps = AmplifyFunctionFactoryBaseProps & Omit<AmplifyFunctionProps, 'absoluteCodePath'> & { | ||
buildCommand: string; | ||
outDir: string; | ||
}; | ||
|
||
// @public (undocumented) | ||
export type AmplifyFunctionFactoryFromDirProps = AmplifyFunctionFactoryBaseProps & Omit<AmplifyFunctionProps, 'absoluteCodePath'> & { | ||
codePath: string; | ||
}; | ||
|
||
// @public | ||
export const Func: typeof AmplifyFunctionFactory; | ||
|
||
// (No @packageDocumentation comment for this package) | ||
|
||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"extends": "../../api-extractor.base.json" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
{ | ||
"name": "@aws-amplify/backend-function", | ||
"version": "0.1.0", | ||
"type": "module", | ||
"exports": { | ||
".": { | ||
"types": "./lib/index.d.ts", | ||
"import": "./lib/index.js", | ||
"require": "./lib/index.js" | ||
} | ||
}, | ||
"types": "lib/index.d.ts", | ||
"scripts": { | ||
"update:api": "api-extractor run --local" | ||
}, | ||
"dependencies": { | ||
"@aws-amplify/function-construct": "^0.1.0", | ||
"execa": "^7.1.1" | ||
}, | ||
"devDependencies": { | ||
"@aws-amplify/backend-engine": "^0.1.0", | ||
"@aws-amplify/plugin-types": "^0.1.0" | ||
}, | ||
"peerDependencies": { | ||
"aws-cdk-lib": "~2.68.0", | ||
"constructs": "^10.0.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { beforeEach, describe, it, mock } from 'node:test'; | ||
import { Func } from './factory.js'; | ||
import { App, Stack } from 'aws-cdk-lib'; | ||
import { | ||
NestedStackResolver, | ||
SingletonConstructContainer, | ||
StackMetadataBackendOutputStorageStrategy, | ||
} from '@aws-amplify/backend-engine'; | ||
import { | ||
BackendOutputEntry, | ||
BackendOutputStorageStrategy, | ||
ConstructContainer, | ||
} from '@aws-amplify/plugin-types'; | ||
import assert from 'node:assert'; | ||
import { fileURLToPath } from 'url'; | ||
|
||
describe('AmplifyFunctionFactory', () => { | ||
let constructContainer: ConstructContainer; | ||
let outputStorageStrategy: BackendOutputStorageStrategy<BackendOutputEntry>; | ||
|
||
beforeEach(() => { | ||
const app = new App(); | ||
const stack = new Stack(app, 'testStack'); | ||
|
||
constructContainer = new SingletonConstructContainer( | ||
new NestedStackResolver(stack) | ||
); | ||
|
||
outputStorageStrategy = new StackMetadataBackendOutputStorageStrategy( | ||
stack | ||
); | ||
}); | ||
|
||
it('creates singleton function instance', () => { | ||
const functionFactory = Func.fromDir({ | ||
name: 'testFunc', | ||
codePath: '../test-assets/test-lambda', | ||
}); | ||
const instance1 = functionFactory.getInstance({ | ||
constructContainer, | ||
outputStorageStrategy, | ||
}); | ||
const instance2 = functionFactory.getInstance({ | ||
constructContainer, | ||
outputStorageStrategy, | ||
}); | ||
assert.strictEqual(instance1, instance2); | ||
}); | ||
|
||
it('executes build command from directory where constructor is used', async () => { | ||
const commandExecutorMock = mock.fn(); | ||
|
||
// Casting to never is necessary because commandExecutor is a private method. | ||
// TS yells that it's not a property on Func even though it is there | ||
mock.method(Func, 'commandExecutor' as never, commandExecutorMock); | ||
|
||
( | ||
await Func.build({ | ||
name: 'testFunc', | ||
outDir: '../test-assets/test-lambda', | ||
buildCommand: 'test command', | ||
}) | ||
).getInstance({ constructContainer, outputStorageStrategy }); | ||
|
||
assert.strictEqual(commandExecutorMock.mock.callCount(), 1); | ||
assert.deepStrictEqual(commandExecutorMock.mock.calls[0].arguments, [ | ||
'test command', | ||
{ | ||
cwd: fileURLToPath(new URL('../src', import.meta.url)), | ||
stdio: 'inherit', | ||
}, | ||
]); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
import { | ||
ConstructContainerEntryGenerator, | ||
ConstructFactory, | ||
ConstructFactoryGetInstanceProps, | ||
} from '@aws-amplify/plugin-types'; | ||
import { | ||
AmplifyFunction, | ||
AmplifyFunctionProps, | ||
} from '@aws-amplify/function-construct'; | ||
import { Construct } from 'constructs'; | ||
import { execaCommand } from 'execa'; | ||
import * as path from 'path'; | ||
import { getCallerDirectory } from './get_caller_directory.js'; | ||
|
||
export type AmplifyFunctionFactoryBaseProps = { | ||
/** | ||
* A name for the function that is used to disambiguate it from other functions in the project | ||
*/ | ||
name: string; | ||
}; | ||
|
||
export type AmplifyFunctionFactoryBuildProps = AmplifyFunctionFactoryBaseProps & | ||
Omit<AmplifyFunctionProps, 'absoluteCodePath'> & { | ||
/** | ||
* The command to run that generates built function code. | ||
* This command is run from the directory where this factory is called | ||
*/ | ||
buildCommand: string; | ||
/** | ||
* The buildCommand is expected to place build artifacts at this location. | ||
* This path can be relative or absolute. If relative, the absolute path is calculated based on the directory where this factory is called | ||
*/ | ||
outDir: string; | ||
}; | ||
|
||
export type AmplifyFunctionFactoryFromDirProps = | ||
AmplifyFunctionFactoryBaseProps & | ||
Omit<AmplifyFunctionProps, 'absoluteCodePath'> & { | ||
/** | ||
* The location of the pre-built function code. | ||
* Can be a directory or a .zip file. | ||
* Can be a relative or absolute path. If relative, the absolute path is calculated based on the directory where this factory is called. | ||
*/ | ||
codePath: string; | ||
}; | ||
|
||
type AmplifyFunctionFactoryProps = AmplifyFunctionFactoryBaseProps & | ||
AmplifyFunctionProps; | ||
|
||
/** | ||
* Create Lambda functions in the context of an Amplify backend definition | ||
*/ | ||
export class AmplifyFunctionFactory | ||
implements ConstructFactory<AmplifyFunction> | ||
{ | ||
// execaCommand is assigned to a static prop so that it can be mocked in tests | ||
private static commandExecutor = execaCommand; | ||
|
||
private generator: ConstructContainerEntryGenerator; | ||
/** | ||
* Create a new AmplifyFunctionFactory | ||
*/ | ||
private constructor(private readonly props: AmplifyFunctionFactoryProps) {} | ||
|
||
/** | ||
* Create a function from a directory that contains pre-built code | ||
*/ | ||
static fromDir( | ||
props: AmplifyFunctionFactoryFromDirProps | ||
): AmplifyFunctionFactory { | ||
const absoluteCodePath = path.isAbsolute(props.codePath) | ||
? props.codePath | ||
: path.resolve(getCallerDirectory(new Error().stack), props.codePath); | ||
return new AmplifyFunctionFactory({ | ||
name: props.name, | ||
absoluteCodePath, | ||
runtime: props.runtime, | ||
handler: props.handler, | ||
}); | ||
} | ||
|
||
/** | ||
* Create a function by executing a build command that places build artifacts at a specified location | ||
* | ||
* TODO: Investigate long-term function building strategy: https://github.com/aws-amplify/samsara-cli/issues/92 | ||
*/ | ||
static async build( | ||
props: AmplifyFunctionFactoryBuildProps | ||
): Promise<AmplifyFunctionFactory> { | ||
const importPath = getCallerDirectory(new Error().stack); | ||
|
||
await AmplifyFunctionFactory.commandExecutor(props.buildCommand, { | ||
cwd: importPath, | ||
stdio: 'inherit', | ||
}); | ||
|
||
const absoluteCodePath = path.isAbsolute(props.outDir) | ||
? props.outDir | ||
: path.resolve(importPath, props.outDir); | ||
|
||
return new AmplifyFunctionFactory({ | ||
name: props.name, | ||
absoluteCodePath, | ||
runtime: props.runtime, | ||
handler: props.handler, | ||
}); | ||
} | ||
|
||
/** | ||
* Creates an instance of AmplifyFunction within the provided Amplify context | ||
*/ | ||
getInstance({ | ||
constructContainer, | ||
}: ConstructFactoryGetInstanceProps): AmplifyFunction { | ||
if (!this.generator) { | ||
this.generator = new AmplifyFunctionGenerator(this.props); | ||
} | ||
return constructContainer.getOrCompute(this.generator) as AmplifyFunction; | ||
} | ||
} | ||
|
||
class AmplifyFunctionGenerator implements ConstructContainerEntryGenerator { | ||
readonly resourceGroupName = 'function'; | ||
|
||
constructor(private readonly props: AmplifyFunctionFactoryProps) {} | ||
|
||
generateContainerEntry(scope: Construct) { | ||
return new AmplifyFunction(scope, this.props.name, this.props); | ||
} | ||
} | ||
|
||
/** | ||
* Alias for AmplifyFunctionFactory | ||
*/ | ||
export const Func = AmplifyFunctionFactory; |
Oops, something went wrong.