diff --git a/commands/video/build.js b/commands/video/build.js index 63dcd7e3..4c85b781 100644 --- a/commands/video/build.js +++ b/commands/video/build.js @@ -8,8 +8,12 @@ module.exports = { name: subcommand, run: async (context) => { const { amplify } = context; - const targetDir = amplify.pathManager.getBackendDirPath(); const amplifyMeta = amplify.getProjectMeta(); + const targetDir = amplify.pathManager.getBackendDirPath(); + const projectDetails = context.amplify.getProjectDetails(); + const newEnvName = projectDetails.localEnvInfo.envName; + const resourceFilesBaseDir = `${targetDir}/video/${shared.resourceName}/`; + const resourceFilesList = fs.readdirSync(resourceFilesBaseDir); if (!(category in amplifyMeta) || Object.keys(amplifyMeta[category]).length === 0) { context.print.error(`You have no ${category} projects.`); @@ -35,7 +39,49 @@ module.exports = { return; } - const props = JSON.parse(fs.readFileSync(`${targetDir}/video/${shared.resourceName}/props.json`)); + // Check if env-specific props file already exists + const hasOwnEnvProps = resourceFilesList.includes(`${newEnvName}-props.json`); + + // Check if ANY props file exist for a different env in this project || returns array + const hasAnyEnvProps = resourceFilesList.find(fileName => fileName.includes('-props.json')); + + // If this env doesn't have its own props AND there is an existing amplify-video resource + if (!hasOwnEnvProps && hasAnyEnvProps) { + // take the first props file you find and copy that! + const propsFilenameToCopy = resourceFilesList.filter(propsFileName => propsFileName.includes('-props.json'))[0]; + + // extract substring for the existing env's name we're going to copy over + const envNameToReplace = propsFilenameToCopy.substr(0, propsFilenameToCopy.indexOf('-')); + + // read JSON from the existing env's props file + const existingPropsToMutate = JSON.parse(fs.readFileSync(`${resourceFilesBaseDir}/${propsFilenameToCopy}`)); + + const searchAndReplaceProps = () => { + const newPropsObj = {}; + // eslint-disable-next-line no-restricted-syntax + for (const [key, value] of Object.entries(existingPropsToMutate.contentDeliveryNetwork)) { + // look for any string values that contain existing env's name + if (typeof value === 'string' && value.includes(`${envNameToReplace}`)) { + // replace with new env name + const newValue = value.replace(new RegExp(envNameToReplace, 'g'), `${newEnvName}`); + newPropsObj[key] = newValue; + } else { + // copy existing values that do not match replacement conditions aka "generic props" + newPropsObj[key] = value; + } + } + return newPropsObj; + }; + + // merge new props and existing generic props + const newPropsToSave = Object.assign( + existingPropsToMutate, { contentDeliveryNetwork: searchAndReplaceProps() }, + ); + + fs.writeFileSync(`${resourceFilesBaseDir}/${newEnvName}-props.json`, JSON.stringify(newPropsToSave, null, 4)); + } + + const props = JSON.parse(fs.readFileSync(`${targetDir}/video/${shared.resourceName}/${projectDetails.localEnvInfo.envName}-props.json`)); return buildTemplates(context, props); }, diff --git a/provider-utils/awscloudformation/cloudformation-templates/ivs-helpers/IVS-Channel.template b/provider-utils/awscloudformation/cloudformation-templates/ivs-helpers/IVS-Channel.template index 65ba08af..02e7ebe5 100644 --- a/provider-utils/awscloudformation/cloudformation-templates/ivs-helpers/IVS-Channel.template +++ b/provider-utils/awscloudformation/cloudformation-templates/ivs-helpers/IVS-Channel.template @@ -35,4 +35,5 @@ Resources: ServiceToken: !Ref pFunctionArn name: !Ref pProjectName type: !Ref pQuality - latencyMode: !Ref pLatencyMode \ No newline at end of file + latencyMode: !Ref pLatencyMode + tags: {"amplify-video" : "amplify-video"} diff --git a/provider-utils/awscloudformation/cloudformation-templates/livestream-helpers/cloudfront-distribution.template b/provider-utils/awscloudformation/cloudformation-templates/livestream-helpers/cloudfront-distribution.template index 4d832746..c62f3b0b 100644 --- a/provider-utils/awscloudformation/cloudformation-templates/livestream-helpers/cloudfront-distribution.template +++ b/provider-utils/awscloudformation/cloudformation-templates/livestream-helpers/cloudfront-distribution.template @@ -166,6 +166,10 @@ "rDistribution" : { "Type": "AWS::CloudFront::Distribution", "Properties": { + "Tags" : [{ + "Key" : "amplify-video", + "Value" : "amplify-video" + }], "DistributionConfig": { "Origins": [ { diff --git a/provider-utils/awscloudformation/cloudformation-templates/vod-helpers/CFDistribution.template.ejs b/provider-utils/awscloudformation/cloudformation-templates/vod-helpers/CFDistribution.template.ejs index 043dd2a7..7ac5388d 100644 --- a/provider-utils/awscloudformation/cloudformation-templates/vod-helpers/CFDistribution.template.ejs +++ b/provider-utils/awscloudformation/cloudformation-templates/vod-helpers/CFDistribution.template.ejs @@ -20,6 +20,9 @@ Resources: rCloudFrontDist: Type: AWS::CloudFront::Distribution Properties: + Tags: + - Key: amplify-video + Value: amplify-video DistributionConfig: DefaultCacheBehavior: ForwardedValues: @@ -40,8 +43,8 @@ Resources: TrustedKeyGroups: - !Ref rCloudFrontKeyGroup <% } %> - Origins: - - + Origins: + - DomainName: !Ref pBucketUrl Id: vodS3Origin S3OriginConfig: @@ -72,4 +75,4 @@ Outputs: <% } %> oCFDomain: Value: !GetAtt rCloudFrontDist.DomainName - Description: Domain for our videos \ No newline at end of file + Description: Domain for our videos diff --git a/provider-utils/awscloudformation/cloudformation-templates/vod-helpers/LambdaFunctions/InputLambda/index.js b/provider-utils/awscloudformation/cloudformation-templates/vod-helpers/LambdaFunctions/InputLambda/index.js index 6fcc87b9..23f2dbe2 100644 --- a/provider-utils/awscloudformation/cloudformation-templates/vod-helpers/LambdaFunctions/InputLambda/index.js +++ b/provider-utils/awscloudformation/cloudformation-templates/vod-helpers/LambdaFunctions/InputLambda/index.js @@ -116,6 +116,7 @@ async function createJob(eventObject) { UserMetadata: {}, Role: process.env.MC_ROLE, Settings: jobSettings, + Tags: { 'amplify-video': 'amplify-video' }, }; await mcClient.createJob(jobParams).promise(); } diff --git a/provider-utils/awscloudformation/index.js b/provider-utils/awscloudformation/index.js index fa99cd8b..4257bdcf 100644 --- a/provider-utils/awscloudformation/index.js +++ b/provider-utils/awscloudformation/index.js @@ -8,6 +8,7 @@ let serviceMetadata; async function addResource(context, service, options) { serviceMetadata = context.amplify.readJsonFile(`${__dirname}/../supported-services.json`)[service]; const targetDir = context.amplify.pathManager.getBackendDirPath(); + const projectDetails = context.amplify.getProjectDetails(); const { serviceWalkthroughFilename, defaultValuesFilename } = serviceMetadata; const serviceWalkthroughSrc = `${__dirname}/service-walkthroughs/${serviceWalkthroughFilename}`; const { serviceQuestions } = require(serviceWalkthroughSrc); @@ -23,13 +24,14 @@ async function addResource(context, service, options) { if (result.parameters !== undefined) { await fs.writeFileSync(`${targetDir}/video/${result.shared.resourceName}/parameters.json`, JSON.stringify(result.parameters, null, 4)); } - await fs.writeFileSync(`${targetDir}/video/${result.shared.resourceName}/props.json`, JSON.stringify(result, null, 4)); + await fs.writeFileSync(`${targetDir}/video/${result.shared.resourceName}/${projectDetails.localEnvInfo.envName}-props.json`, JSON.stringify(result, null, 4)); await buildTemplates(context, result); } async function updateResource(context, service, options, resourceName) { serviceMetadata = context.amplify.readJsonFile(`${__dirname}/../supported-services.json`)[service]; const targetDir = context.amplify.pathManager.getBackendDirPath(); + const projectDetails = context.amplify.getProjectDetails(); const { serviceWalkthroughFilename, defaultValuesFilename } = serviceMetadata; const serviceWalkthroughSrc = `${__dirname}/service-walkthroughs/${serviceWalkthroughFilename}`; const { serviceQuestions } = require(serviceWalkthroughSrc); @@ -37,7 +39,7 @@ async function updateResource(context, service, options, resourceName) { if (result.parameters !== undefined) { await fs.writeFileSync(`${targetDir}/video/${result.shared.resourceName}/parameters.json`, JSON.stringify(result.parameters, null, 4)); } - await fs.writeFileSync(`${targetDir}/video/${result.shared.resourceName}/props.json`, JSON.stringify(result, null, 4)); + await fs.writeFileSync(`${targetDir}/video/${result.shared.resourceName}/${projectDetails.localEnvInfo.envName}-props.json`, JSON.stringify(result, null, 4)); await buildTemplates(context, result); context.print.success(`Successfully updated ${result.shared.resourceName}`); } diff --git a/provider-utils/awscloudformation/schemas/schema.graphql.ejs b/provider-utils/awscloudformation/schemas/schema.graphql.ejs index 836c1a78..ff224986 100644 --- a/provider-utils/awscloudformation/schemas/schema.graphql.ejs +++ b/provider-utils/awscloudformation/schemas/schema.graphql.ejs @@ -1,6 +1,6 @@ -type vodAsset @model (subscriptions: {level: public}) +type VodAsset @model (subscriptions: {level: public}) @auth( rules: [ <% if (locals.permissions && locals.permissions.permissionSchema.includes("any")) { -%> @@ -21,11 +21,11 @@ type vodAsset @model (subscriptions: {level: public}) description:String! #DO NOT EDIT - video:videoObject @connection -} + video:VideoObject @connection +} #DO NOT EDIT -type videoObject @model +type VideoObject @model @auth( rules: [ <% if (locals.permissions && locals.permissions.permissionSchema.includes("any")) { -%> @@ -45,4 +45,4 @@ type videoObject @model <% if (contentDeliveryNetwork.signedKey) { -%> token: String @function(name: "<%= contentDeliveryNetwork.functionNameSchema %>") <% } -%> -} \ No newline at end of file +} diff --git a/provider-utils/awscloudformation/service-walkthroughs/livestream-push.js b/provider-utils/awscloudformation/service-walkthroughs/livestream-push.js index 1f20cd73..225f78cc 100644 --- a/provider-utils/awscloudformation/service-walkthroughs/livestream-push.js +++ b/provider-utils/awscloudformation/service-walkthroughs/livestream-push.js @@ -13,6 +13,7 @@ async function serviceQuestions(context, options, defaultValuesFilename, resourc const defaultLocation = path.resolve(`${__dirname}/../default-values/${defaultValuesFilename}`); let resource = {}; const targetDir = amplify.pathManager.getBackendDirPath(); + const projectDetails = context.amplify.getProjectDetails(); let advancedAnswers = {}; let mediaLiveAnswers = {}; let mediaPackageAnswers; @@ -24,7 +25,7 @@ async function serviceQuestions(context, options, defaultValuesFilename, resourc // TODO: find a way to use default in new question files try { - const oldValues = JSON.parse(fs.readFileSync(`${targetDir}/video/${resourceName}/props.json`)); + const oldValues = JSON.parse(fs.readFileSync(`${targetDir}/video/${resourceName}/${projectDetails.localEnvInfo.envName}-props.json`)); Object.assign(defaults, oldValues); } catch (err) { // Do nothing diff --git a/provider-utils/awscloudformation/service-walkthroughs/vod-push.js b/provider-utils/awscloudformation/service-walkthroughs/vod-push.js index 300a4353..b12863fe 100644 --- a/provider-utils/awscloudformation/service-walkthroughs/vod-push.js +++ b/provider-utils/awscloudformation/service-walkthroughs/vod-push.js @@ -49,7 +49,7 @@ async function serviceQuestions(context, options, defaultValuesFilename, resourc props.shared = nameDict; // TODO: find a way of using default values from new question try { - oldValues = JSON.parse(fs.readFileSync(`${targetDir}/video/${resourceName}/props.json`)); + oldValues = JSON.parse(fs.readFileSync(`${targetDir}/video/${resourceName}/${projectDetails.localEnvInfo.envName}-props.json`)); Object.assign(defaults, oldValues); } catch (err) { // Do nothing @@ -340,7 +340,7 @@ async function createCDN(context, props, options, aws, oldValues) { const uuid = Math.random().toString(36).substring(2, 6) + Math.random().toString(36).substring(2, 6); const secretName = `${props.shared.resourceName}-${projectDetails.localEnvInfo.envName}-pem-${uuid}`.slice(0, 63); - const rPublicName = `rCloudFrontPublicKey${uuid}`.slice(0, 63); + const rPublicName = `rCloudFrontPublicKey${projectDetails.localEnvInfo.envName}${uuid}`.slice(0, 63); const publicKeyName = `${props.shared.resourceName}-${projectDetails.localEnvInfo.envName}-publickey-${uuid}`.slice(0, 63); const smClient = new aws.SecretsManager({ apiVersion: '2017-10-17' }); const createSecretParams = { diff --git a/provider-utils/awscloudformation/utils/livestream-obs.js b/provider-utils/awscloudformation/utils/livestream-obs.js index c255edad..4fcfdc6d 100644 --- a/provider-utils/awscloudformation/utils/livestream-obs.js +++ b/provider-utils/awscloudformation/utils/livestream-obs.js @@ -19,6 +19,7 @@ async function setupOBS(context, resourceName) { async function createConfig(context, projectConfig, projectName) { // check for obs installation! let profileDir = ''; + const projectDetails = context.amplify.getProjectDetails(); if (process.platform === 'darwin') { profileDir = `${process.env.HOME}/Library/Application Support/obs-studio/basic/profiles/`; } else if (process.platform === 'win32') { @@ -45,7 +46,7 @@ async function createConfig(context, projectConfig, projectName) { generateServiceLive(profileDir, projectConfig.output.oMediaLivePrimaryIngestUrl); } else if (projectConfig.serviceType === 'ivs') { const targetDir = context.amplify.pathManager.getBackendDirPath(); - const props = JSON.parse(fs.readFileSync(`${targetDir}/video/${projectName}/props.json`)); + const props = JSON.parse(fs.readFileSync(`${targetDir}/video/${projectName}/${projectDetails.localEnvInfo.envName}-props.json`)); generateINIIVS(projectName, profileDir, props); generateServiceIVS(profileDir, projectConfig.output); } diff --git a/provider-utils/awscloudformation/utils/video-staging.js b/provider-utils/awscloudformation/utils/video-staging.js index 1a3c639f..63e8a705 100644 --- a/provider-utils/awscloudformation/utils/video-staging.js +++ b/provider-utils/awscloudformation/utils/video-staging.js @@ -51,8 +51,57 @@ async function pushTemplates(context) { async function build(context, resourceName, projectType, props) { const { amplify } = context; const targetDir = amplify.pathManager.getBackendDirPath(); + const projectDetails = context.amplify.getProjectDetails(); + + const newEnvName = projectDetails.localEnvInfo.envName; + const resourceFilesBaseDir = `${targetDir}/video/${resourceName}/`; + const resourceFilesList = fs.readdirSync(resourceFilesBaseDir); + + + // Check if env-specific props file already exists + const hasOwnEnvProps = resourceFilesList.includes(`${newEnvName}-props.json`); + + // Check if ANY props file exist for a different env in this project || returns array + const hasAnyEnvProps = resourceFilesList.find(fileName => fileName.includes('-props.json')); + + // If this env doesn't have its own props AND there is an existing amplify-video resource + if (!hasOwnEnvProps && hasAnyEnvProps) { + // take the first props file you find and copy that! + const propsFilenameToCopy = resourceFilesList.filter(propsFileName => propsFileName.includes('-props.json'))[0]; + + // extract substring for the existing env's name we're going to copy over + const envNameToReplace = propsFilenameToCopy.substr(0, propsFilenameToCopy.indexOf('-')); + + // read JSON from the existing env's props file + const existingPropsToMutate = JSON.parse(fs.readFileSync(`${resourceFilesBaseDir}/${propsFilenameToCopy}`)); + + const searchAndReplaceProps = () => { + const newPropsObj = {}; + // eslint-disable-next-line no-restricted-syntax + for (const [key, value] of Object.entries(existingPropsToMutate.contentDeliveryNetwork)) { + // look for any string values that contain existing env's name + if (typeof value === 'string' && value.includes(`${envNameToReplace}`)) { + // replace with new env name + const newValue = value.replace(new RegExp(envNameToReplace, 'g'), `${newEnvName}`); + newPropsObj[key] = newValue; + } else { + // copy existing values that do not match replacement conditions aka "generic props" + newPropsObj[key] = value; + } + } + return newPropsObj; + }; + + // merge new props and existing generic props + const newPropsToSave = Object.assign( + existingPropsToMutate, { contentDeliveryNetwork: searchAndReplaceProps() }, + ); + + fs.writeFileSync(`${resourceFilesBaseDir}/${newEnvName}-props.json`, JSON.stringify(newPropsToSave, null, 4)); + } + if (!props) { - props = JSON.parse(fs.readFileSync(`${targetDir}/video/${resourceName}/props.json`)); + props = JSON.parse(fs.readFileSync(`${targetDir}/video/${resourceName}/${projectDetails.localEnvInfo.envName}-props.json`)); } if (projectType === 'video-on-demand') { props = getVODEnvVars(context, props, resourceName); @@ -95,7 +144,7 @@ function getVODEnvVars(context, props, resourceName) { delete props.shared.bucket; delete props.shared.bucketInput; delete props.shared.bucketOutput; - fs.writeFileSync(`${targetDir}/video/${resourceName}/props.json`, JSON.stringify(props, null, 4)); + fs.writeFileSync(`${targetDir}/video/${resourceName}/${amplifyProjectDetails.localEnvInfo.envName}-props.json`, JSON.stringify(props, null, 4)); } const envVars = amplifyProjectDetails.teamProviderInfo[currentEnvInfo] .categories.video[resourceName]; diff --git a/test.js b/test.js new file mode 100644 index 00000000..7f600189 --- /dev/null +++ b/test.js @@ -0,0 +1,47 @@ +/* eslint-disable guard-for-in */ +/* eslint-disable no-restricted-syntax */ +const fs = require('fs'); + +const newEnvName = 'tonia'; +const resourceDirectoryFiles = fs.readdirSync( + `${__dirname}/`, +); + +// Check if env-specific props file already exists +const hasOwnEnvProps = resourceDirectoryFiles.includes(`${newEnvName}-props.json`); + +// Check if ANY env props exist +const hasAnyEnvProps = resourceDirectoryFiles.find(item => item.includes('-props.json')); + +// console.log('hasOwnEnv', hasOwnEnvProps); +// console.log('hasAnyEnvProps', hasAnyEnvProps); + +if (!hasOwnEnvProps) { + if (hasAnyEnvProps) { + // take the first props file you find and copy that! + const propsFilenameToCopy = resourceDirectoryFiles.filter(propsFileName => propsFileName.includes('-props.json'))[0]; + const envNameToReplace = propsFilenameToCopy.substr(0, propsFilenameToCopy.indexOf('-')); + const propsToMutate = JSON.parse(fs.readFileSync(`${__dirname}/${propsFilenameToCopy}`)); + + const searchAndReplaceProps = () => { + const newPropsObj = {}; + for (const [key, value] of Object.entries(propsToMutate.contentDeliveryNetwork)) { + if (typeof value === 'string' && value.includes(`${envNameToReplace}`)) { + const newValue = value.replace(new RegExp(envNameToReplace, 'g'), `${newEnvName}`); + newPropsObj[key] = newValue; + } else { + newPropsObj[key] = value; + } + } + return newPropsObj; + }; + + const newPropsToSave = Object.assign( + propsToMutate, { contentDeliveryNetwork: searchAndReplaceProps() }, + ); + + console.log('IM GONNA SAVE THIS, ', newPropsToSave); + + fs.writeFileSync(`${__dirname}/${newEnvName}-props.json`, JSON.stringify(newPropsToSave, null, 4)); + } +}