-
Notifications
You must be signed in to change notification settings - Fork 2
Plugin Architecture Proposal
Amplify CLI is built such that it can be extended using plugins. All the categories and providers are plugins. It uses gluegun to support plugins. Gluegun plugin system allows users to easily add command using plugins. It exposes an context/toolbox object for each plugin command.
Gluegun makes it easy to add new commands to CLI using a plugin. Currently each category provides a new command to support a functionality. But the number of commands each category needs to support are
- add
- remove
- update
- configure
Each category is designed such that they could support multiple providers. But the core support only cloudformation as a provider. If anyone wants to add an additional provider, they have either have to write a new plugin with a different name or contribute code back to core plugin if they want to keep the name of the command common. For instance, if someone wants to add auth which uses a different provider, then they either have to create a new plugin amplify-category-auth-foo
and the command that they will have to run amplify auth-foo add
instead of running amplify auth add
. If they want to support amplify auth add
then they will have to contribute their plugin data to amplify-category-auth
which means that core team will have to either give access to the repo or take up the maintenance.
The Gluegun plugin system does not enforce any consistent interface implementation on any plugin. Each and every plugin can access configuration value of other plugin and can update it without realizing it might be overwriting some changes, which makes it harder to debug
Each plugin gets context/toolbox object when they are invoked. There is no guarantee what methods are available to each plugin as it depends on the plugins instantiated.
No dependency management. If a plugin depends on another plugin, they have to manage the dependency on their own.
Plugin loading is slower. It involves loading all the commands that are in the plugin directory
The CLI would support 3 types of plugin as it already does
- Provider
- Category
- Frontend
- General purpose
Each plugin will describe what it provides and what are their dependencies in package.json
{
"name": "amplify-category-foo",
"version": "0.1.0",
"dependencies": {},
"amplify": {
"name": "amplify-category-name",
"type": "category",
"depends":
["amplify-category-bar", "aws-cloudformation"]
}
}
A plugin will be loaded only if they have all the dependencies avaliable.
// ResourceManager
class ResourceManager {
private pluginName: string;
private env: string;
constructor(pluginName: string, env: string) {
}
loadResource(resourceName: string): object {
return {}
}
saveResource(resourceName: string, value: object): void {
}
removeResource(resourceName: string): void {
}
addEnvSpecifcResource(resourceName: string): object {
return {}
}
saveEnvSpecificResource(resourceName: string, value: object): void {
}
removeEnvSpecificResource(resourceName: string): void {
}
}
class AmplifyPlugin {
public name: string
private deps: { name: string, value: AmplifyCategory}
private config: object
constructor(deps, config) {
}
}
class AmplifyProviderPlugin extends AmplifyPlugin {
private resourceManager: ResourceManager;
constructor (config: {}, resourceManager: ResourceManager) {
super(config);
this.resourceManager = resourceManager;
}
init: () => void
initEnv: () => void
push: (resourceName?: string) => void
addResource?: (category: string, resourceName: string, resource: object) => void
addEnvSpecificResource?: (category: string, resourceName: string, resource: object) => void
configure: () => void
deleteEnv: (envName: string) => void
deleteProject:() => void
}
class AmplifyCategoryPlugin extends AmplifyPlugin {
add?:(params: object) => void
remove?:(params: object) => void
update?:(params: object) => void
ensureCategory?: (spec?: object) => void
}
class AmplifyFrontendPlugin extends AmplifyPlugin {
init: () => void
scanProject: () => void
configure:() => void
publish:() => void
generateFrontendConfigs: () => void
}