const assert = require('assert')
const _ = require('lodash');
const AWSObject = require('./aws-object').AWSObject
const Policy = require('./policy').Policy;
/**
* A super class to represent an AWS component.
* Some functions are to be implemented by sub classes.
* @abstract
*/
class AWSComponent extends AWSObject {
/**
* Don't call this manually. Use creator function in {@link CloudFormation}
* @param {string} stackName
* @param {string} baseName
*/
constructor(stackName, baseName) {
super(stackName, baseName)
this.policyStatements = []
this.assumeRolePolicyDocument = null
this._crossStackValues = {}
}
get crossStackValues() {
return _.clone(this._crossStackValues)
}
setCrossStackValue(name, value) {
this._crossStackValues[name] = value
}
get sameStackValues() {
return {
'ID': { "Ref": this.fullName },
'ARN': {
"Fn::GetAtt": [
this.fullName,
"Arn"
]
}
}
}
/**
* Get stack value of this component, variables can be:
* ARN, ID, PROVIDERNAME, etc
* If this component is deployed and checked, the actual value will be provided.
* Otherwise, AWS CloudFormation intrinsic specs will be returned, such as {"Ref": <logicName>}
*
* @param {String} key ARN | ID | PROVIDERNAME
*/
getValue(key) {
return this.crossStackValues[key] || this.sameStackValues[key]
}
get outputVariableKeys() {
return _.keys(this.outputSpecs)
}
get outputSpecs() {
//Default: get ARN and ID
let specs = {}
const id_spec = `${this.stackName}${this.fullName}ID`;
specs[id_spec] = {
"Description": `The ID for resource ${this.fullName} of stack ${this.stackName}`,
"Value": { "Ref": this.fullName },
"Export": {
"Name": id_spec
}
}
const arn_spec = `${this.stackName}${this.fullName}ARN`;
specs[arn_spec] = {
"Description": `The ARN for resource ${this.fullName} of stack ${this.stackName}`,
"Value": {
"Fn::GetAtt": [
this.fullName,
"Arn"
]
},
"Export": {
"Name": arn_spec
}
}
return specs
}
roleName() {
const theRole = this.defaultRole;
return theRole ? theRole.fullName : null;
}
getDefaultRole() {
console.warn('Deprecated. Use .defaultRole() instead');
return this.defaultRole;
}
get defaultRole() {
if (!_.isEmpty(this.policyStatements) && this.assumeRolePolicyDocument) {
const Role = require('./role').Role;
let role = new Role(this.stackName, this.fullName)
role.policyStatements = this.policyStatements
if (this.assumeRolePolicyDocument) {
role.assumeRolePolicyDocument = this.assumeRolePolicyDocument
}
return role
}
else {
return null;
}
}
/**
* Get the policy statement object for the given access levels
* @returns {object} policy statement JSON
* @param {array} accessLevels array with string element which possible values are: ACCESS_LEVEL_READ | ACCESS_LEVEL_WRITE | ACCESS_LEVEL_ADMIN
* @param {string} item optional
*/
policyStatementForAccess(accessLevels, item) {
accessLevels.forEach((accessLevel) => {
assert.ok([AWSComponent.ACCESS_LEVEL_READ,
AWSComponent.ACCESS_LEVEL_WRITE,
AWSComponent.ACCESS_LEVEL_ADMIN
].indexOf(accessLevel) >= 0, `Invalid access level: ${accessLevel}`)
})
return this.policyStatementForAccessImpl(accessLevels, item)
}
/**
* Get the policy statement object for the given access levels. Should be implemented by sub classes.
* @abstract
* @param {array} accessLevels array with string element which possible values are: ACCESS_LEVEL_READ | ACCESS_LEVEL_WRITE | ACCESS_LEVEL_ADMIN
* @param {string} item optional
*/
policyStatementForAccessImpl(accessLevels, item) {
throw new Error('This function must be overriden in sub classes')
}
}
//Static fields
AWSComponent.ACCESS_LEVEL_READ = 'ACCESS_LEVEL_READ'
AWSComponent.ACCESS_LEVEL_WRITE = 'ACCESS_LEVEL_WRITE'
AWSComponent.ACCESS_LEVEL_ADMIN = 'ACCESS_LEVEL_ADMIN'
exports.AWSComponent = AWSComponent