diff --git a/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/__tests__/aws-account-mgmt-plugin.test.js b/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/__tests__/aws-account-mgmt-plugin.test.js
new file mode 100644
index 0000000000..aef8065241
--- /dev/null
+++ b/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/__tests__/aws-account-mgmt-plugin.test.js
@@ -0,0 +1,165 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+const ServicesContainer = require('@aws-ee/base-services-container/lib/services-container');
+
+// Mocked dependencies
+jest.mock('@aws-ee/base-services/lib/plugin-registry/plugin-registry-service');
+const PluginRegistryService = require('@aws-ee/base-services/lib/plugin-registry/plugin-registry-service');
+
+jest.mock('@aws-ee/base-services/lib/settings/env-settings-service');
+const SettingsServiceMock = require('@aws-ee/base-services/lib/settings/env-settings-service');
+
+jest.mock('@aws-ee/base-raas-services/lib/environment/service-catalog/environment-sc-service');
+const EnvironmentScServiceMock = require('@aws-ee/base-raas-services/lib/environment/service-catalog/environment-sc-service');
+
+jest.mock('@aws-ee/base-raas-services/lib/indexes/indexes-service');
+const IndexesServiceMock = require('@aws-ee/base-raas-services/lib/indexes/indexes-service');
+
+const plugin = require('../aws-account-mgmt-plugin');
+
+// CHECKed Functions: getActiveNonAppStreamEnvs
+describe('awsAccountMgmtPlugin', () => {
+ let container;
+ let settings;
+ let environmentScService;
+ let indexesService;
+ beforeEach(async () => {
+ // Initialize services container and register dependencies
+ container = new ServicesContainer();
+ container.register('pluginRegistryService', new PluginRegistryService());
+ container.register('settings', new SettingsServiceMock());
+ container.register('environmentScService', new EnvironmentScServiceMock());
+ container.register('indexesService', new IndexesServiceMock());
+
+ await container.initServices();
+ settings = await container.find('settings');
+ environmentScService = await container.find('environmentScService');
+ indexesService = await container.find('indexesService');
+ });
+
+ describe('getActiveNonAppStreamEnvs', () => {
+ const requestContext = { principalIdentifier: { uid: 'u-testuser' } };
+ it('should return empty list if AppStream is disabled', async () => {
+ // BUILD
+ const awsAccountId = 'sampleAwsAccountId';
+ settings.getBoolean = jest.fn(() => {
+ return false;
+ });
+ const expected = [];
+
+ // OPERATE
+ const retVal = await plugin.getActiveNonAppStreamEnvs({ awsAccountId }, { requestContext, container });
+
+ // CHECK
+ expect(retVal).toEqual(expected);
+ });
+
+ it('should return a list of active non-AppStream environments for an account if AppStream is enabled', async () => {
+ // BUILD
+ const awsAccountId = 'sampleAwsAccountId';
+ settings.getBoolean = jest.fn(() => {
+ return true;
+ });
+ const scEnvs = [
+ { id: 'env1', indexId: 'index1', isAppStreamConfigured: true, status: 'COMPLETED' },
+ { id: 'env2', indexId: 'index1', isAppStreamConfigured: false, status: 'COMPLETED' }, // This will be returned
+ { id: 'env3', indexId: 'index1', isAppStreamConfigured: false, status: 'FAILED' },
+ { id: 'env4', indexId: 'index1', isAppStreamConfigured: false, status: 'TERMINATED' },
+ { id: 'env5', indexId: 'index1', isAppStreamConfigured: false, status: 'UNKNOWN' },
+ ];
+ const indexes = [
+ { id: 'index1', awsAccountId },
+ { id: 'index2', awsAccountId: 'someOtherAccount' },
+ ];
+ environmentScService.list = jest.fn(() => {
+ return scEnvs;
+ });
+ indexesService.list = jest.fn(() => {
+ return indexes;
+ });
+
+ const expected = [{ id: 'env2', indexId: 'index1', isAppStreamConfigured: false, status: 'COMPLETED' }];
+
+ // OPERATE
+ const retVal = await plugin.getActiveNonAppStreamEnvs({ awsAccountId }, { requestContext, container });
+
+ // CHECK
+ expect(retVal).toEqual(expected);
+ });
+
+ it('should return an empty list if no active non-AppStream environments for an account are found', async () => {
+ // BUILD
+ const awsAccountId = 'sampleAwsAccountId';
+ settings.getBoolean = jest.fn(() => {
+ return true;
+ });
+ const scEnvs = [
+ { id: 'env1', indexId: 'index1', isAppStreamConfigured: true, status: 'COMPLETED' },
+ { id: 'env2', indexId: 'index1', isAppStreamConfigured: false, status: 'TERMINATED' },
+ { id: 'env3', indexId: 'index1', isAppStreamConfigured: false, status: 'FAILED' },
+ { id: 'env4', indexId: 'index1', isAppStreamConfigured: false, status: 'UNKNOWN' },
+ ];
+ const indexes = [
+ { id: 'index1', awsAccountId },
+ { id: 'index2', awsAccountId: 'someOtherAccount' },
+ ];
+ environmentScService.list = jest.fn(() => {
+ return scEnvs;
+ });
+ indexesService.list = jest.fn(() => {
+ return indexes;
+ });
+
+ const expected = [];
+
+ // OPERATE
+ const retVal = await plugin.getActiveNonAppStreamEnvs({ awsAccountId }, { requestContext, container });
+
+ // CHECK
+ expect(retVal).toEqual(expected);
+ });
+
+ it('should return an empty list if active non-AppStream environments exist but for a different account', async () => {
+ // BUILD
+ const awsAccountId = 'sampleAwsAccountId';
+ settings.getBoolean = jest.fn(() => {
+ return true;
+ });
+ const scEnvs = [
+ { id: 'env1', indexId: 'index1', isAppStreamConfigured: true, status: 'COMPLETED' },
+ { id: 'env2', indexId: 'index1', isAppStreamConfigured: false, status: 'STOPPED' },
+ ];
+ const indexes = [
+ { id: 'index1', awsAccountId: 'someOtherAccount' },
+ { id: 'index2', awsAccountId },
+ ];
+ environmentScService.list = jest.fn(() => {
+ return scEnvs;
+ });
+ indexesService.list = jest.fn(() => {
+ return indexes;
+ });
+
+ const expected = [];
+
+ // OPERATE
+ const retVal = await plugin.getActiveNonAppStreamEnvs({ awsAccountId }, { requestContext, container });
+
+ // CHECK
+ expect(retVal).toEqual(expected);
+ });
+ });
+});
diff --git a/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/__tests__/env-sc-connection-url-plugin.test.js b/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/__tests__/env-sc-connection-url-plugin.test.js
index 592aaa0009..64dec8baf3 100644
--- a/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/__tests__/env-sc-connection-url-plugin.test.js
+++ b/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/__tests__/env-sc-connection-url-plugin.test.js
@@ -25,15 +25,15 @@ const PluginRegistryService = require('@aws-ee/base-services/lib/plugin-registry
jest.mock('@aws-ee/base-services/lib/settings/env-settings-service');
const SettingsServiceMock = require('@aws-ee/base-services/lib/settings/env-settings-service');
-jest.mock('../../appstream/appstream-sc-service');
-const AppStreamScService = require('../../appstream/appstream-sc-service');
-
jest.mock('@aws-ee/base-raas-services/lib/environment/service-catalog/environment-sc-connection-service');
const EnvironmentScConnectionServiceMock = require('@aws-ee/base-raas-services/lib/environment/service-catalog/environment-sc-connection-service');
+jest.mock('../../appstream/appstream-sc-service');
+const AppStreamScService = require('../../appstream/appstream-sc-service');
+
const plugin = require('../env-sc-connection-url-plugin');
-// Tested Functions: create, update, delete
+// Tested Functions: createConnectionUrl
describe('envScConnectionUrlPlugin', () => {
let container;
let appStreamScService;
@@ -60,7 +60,7 @@ describe('envScConnectionUrlPlugin', () => {
// BUILD
const connection = { scheme: 'http', operation: 'create' };
const envId = 'sampleEnvId';
- settings.optionalBoolean = jest.fn(() => {
+ settings.getBoolean = jest.fn(() => {
return false;
});
@@ -75,7 +75,7 @@ describe('envScConnectionUrlPlugin', () => {
// BUILD
const connection = { scheme: 'http', operation: 'list' };
const envId = 'sampleEnvId';
- settings.optionalBoolean = jest.fn(() => {
+ settings.getBoolean = jest.fn(() => {
return true;
});
@@ -90,7 +90,7 @@ describe('envScConnectionUrlPlugin', () => {
// BUILD
const connection = { scheme: 'random', operation: 'create' };
const envId = 'sampleEnvId';
- settings.optionalBoolean = jest.fn(() => {
+ settings.getBoolean = jest.fn(() => {
return true;
});
appStreamScService.getStreamingUrl = jest.fn();
@@ -110,7 +110,7 @@ describe('envScConnectionUrlPlugin', () => {
const destinationUrl = 'originalPublicDestinationUrl';
let connection = { scheme: 'http', operation: 'create', url: destinationUrl, type: 'SageMaker' };
const envId = 'sampleEnvId';
- settings.optionalBoolean = jest.fn(() => {
+ settings.getBoolean = jest.fn(() => {
return true;
});
environmentScConnectionService.createPrivateSageMakerUrl = jest.fn(() => {
@@ -152,7 +152,7 @@ describe('envScConnectionUrlPlugin', () => {
const destinationUrl = 'destinationUrl';
let connection = { scheme: 'http', operation: 'create', url: destinationUrl };
const envId = 'sampleEnvId';
- settings.optionalBoolean = jest.fn(() => {
+ settings.getBoolean = jest.fn(() => {
return true;
});
const streamingUrl = 'sampleAppStreamUrl';
@@ -185,7 +185,7 @@ describe('envScConnectionUrlPlugin', () => {
const destinationUrl = 'destinationUrl';
let connection = { scheme: 'ssh', operation: 'create', url: destinationUrl };
const envId = 'sampleEnvId';
- settings.optionalBoolean = jest.fn(() => {
+ settings.getBoolean = jest.fn(() => {
return true;
});
const streamingUrl = 'sampleAppStreamUrl';
@@ -216,7 +216,7 @@ describe('envScConnectionUrlPlugin', () => {
// BUILD
let connection = { scheme: 'rdp', operation: 'create', instanceId: 'sampleInstanceId' };
const envId = 'sampleEnvId';
- settings.optionalBoolean = jest.fn(() => {
+ settings.getBoolean = jest.fn(() => {
return true;
});
const streamingUrl = 'sampleAppStreamUrl';
diff --git a/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/aws-account-mgmt-plugin.js b/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/aws-account-mgmt-plugin.js
new file mode 100644
index 0000000000..5033ead9f8
--- /dev/null
+++ b/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/aws-account-mgmt-plugin.js
@@ -0,0 +1,57 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+const _ = require('lodash');
+
+const settingKeys = {
+ isAppStreamEnabled: 'isAppStreamEnabled',
+};
+
+/**
+ * Returns a list of active non-AppStream environments linked to a given AWS Account ID
+ * This check is only performed when the deployment has AppStream enabled,
+ * and is triggered if the user attempts to update the AWS account using SWB APIs.
+ * A similar check is performed on the UI components (AccountUtils) as well.
+ */
+async function getActiveNonAppStreamEnvs({ awsAccountId }, { requestContext, container }) {
+ const settings = await container.find('settings');
+ const isAppStreamEnabled = settings.getBoolean(settingKeys.isAppStreamEnabled);
+ if (!isAppStreamEnabled) return [];
+
+ const nonActiveStates = ['FAILED', 'TERMINATED', 'UNKNOWN'];
+ const environmentScService = await container.find('environmentScService');
+ const indexesService = await container.find('indexesService');
+
+ const indexes = await indexesService.list(requestContext);
+ const indexesIdsOfInterest = _.map(
+ _.filter(indexes, index => index.awsAccountId === awsAccountId),
+ 'id',
+ );
+
+ const scEnvs = await environmentScService.list(requestContext);
+ const retVal = _.filter(
+ scEnvs,
+ scEnv =>
+ _.includes(indexesIdsOfInterest, scEnv.indexId) &&
+ !scEnv.isAppStreamConfigured &&
+ !_.includes(nonActiveStates, scEnv.status),
+ );
+
+ return retVal;
+}
+
+const plugin = { getActiveNonAppStreamEnvs };
+
+module.exports = plugin;
diff --git a/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/env-sc-connection-url-plugin.js b/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/env-sc-connection-url-plugin.js
index a83e035928..398d453288 100644
--- a/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/env-sc-connection-url-plugin.js
+++ b/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/env-sc-connection-url-plugin.js
@@ -28,7 +28,7 @@ async function createConnectionUrl({ envId, connection }, { requestContext, cont
const appStreamScService = await container.find('appStreamScService');
const environmentScConnectionService = await container.find('environmentScConnectionService');
const settings = await container.find('settings');
- const isAppStreamEnabled = settings.optionalBoolean(settingKeys.isAppStreamEnabled, false);
+ const isAppStreamEnabled = settings.getBoolean(settingKeys.isAppStreamEnabled);
// This plugin will only contribute to URL creation when AppStream is enabled
// Since this plugin is also called upon during listConnections cycle
diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/models/environments-sc/ScEnvironment.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/models/environments-sc/ScEnvironment.js
index 71b174111a..4c0130dde8 100644
--- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/models/environments-sc/ScEnvironment.js
+++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/models/environments-sc/ScEnvironment.js
@@ -167,6 +167,7 @@ const ScEnvironment = types
studyIds: types.frozen([]),
cidr: types.frozen([]),
outputs: types.frozen([]),
+ isAppStreamConfigured: types.optional(types.boolean, false),
})
.actions(self => ({
setScEnvironment(rawEnvironment) {
diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/accounts/AccountCard.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/accounts/AccountCard.js
index 0ec912bb94..42a08cf23d 100644
--- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/accounts/AccountCard.js
+++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/accounts/AccountCard.js
@@ -19,10 +19,9 @@ import { observer, inject } from 'mobx-react';
import { withRouter } from 'react-router-dom';
import { Header, Segment, Accordion, Icon, Label, Table, Button } from 'semantic-ui-react';
import c from 'classnames';
-
import { createLink } from '@aws-ee/base-ui/dist/helpers/routing';
-
import { displayWarning } from '@aws-ee/base-ui/dist/helpers/notification';
+import { isAppStreamEnabled } from '../../helpers/settings';
const { getAccountIdsOfActiveEnvironments } = require('./AccountUtils');
@@ -54,12 +53,8 @@ class AccountCard extends React.Component {
return this.props.account;
}
- get isAppStreamEnabled() {
- return process.env.REACT_APP_IS_APP_STREAM_ENABLED === 'true';
- }
-
get appStreamStatusMismatch() {
- return this.isAppStreamEnabled && !this.account.isAppStreamConfigured;
+ return isAppStreamEnabled && !this.account.isAppStreamConfigured;
}
get awsAccountsStore() {
@@ -181,7 +176,7 @@ class AccountCard extends React.Component {
: `Service Workbench is waiting for the CFN stack to complete.
Please wait a few minutes for provisioning to complete.
If you did not create a CFN stack for this account, click
- "Re-Onboard Account" to return to the account onboarding page.`}
+ "Re-Onboard Account" to return to the account onboarding page.`}
)}
diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/accounts/AccountUtils.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/accounts/AccountUtils.js
index 7e3c892962..da1d0ccbcf 100644
--- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/accounts/AccountUtils.js
+++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/accounts/AccountUtils.js
@@ -1,9 +1,9 @@
import _ from 'lodash';
function getAccountIdsOfActiveEnvironments(scEnvs, projects, indexes) {
- const nonActivateStates = ['FAILED', 'TERMINATED', 'UNKNOWN'];
+ const nonActiveStates = ['FAILED', 'TERMINATED', 'UNKNOWN'];
const activeEnvs = scEnvs.filter(env => {
- return !nonActivateStates.includes(env.status);
+ return !nonActiveStates.includes(env.status);
});
const projectToActiveEnvs = _.groupBy(activeEnvs, 'projectId');
diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/ScEnvironmentCard.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/ScEnvironmentCard.js
index c42d23f5bf..84e9721cae 100644
--- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/ScEnvironmentCard.js
+++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/ScEnvironmentCard.js
@@ -7,6 +7,7 @@ import { Header, Label, Popup, Icon, Divider, Message, Table, Grid, Segment } fr
import TimeAgo from 'react-timeago';
import { niceNumber } from '@aws-ee/base-ui/dist/helpers/utils';
+import { isAppStreamEnabled } from '../../helpers/settings';
import By from '../helpers/By';
import ScEnvironmentButtons from './parts/ScEnvironmentButtons';
import ScEnvironmentCost from './parts/ScEnvironmentCost';
@@ -40,6 +41,7 @@ class ScEnvironmentCard extends React.Component {
{this.renderStatus(state)}
{this.renderTitle(env)}
{this.renderError(env)}
+ {this.renderWarning(env)}
{this.renderButtons(env)}
@@ -120,6 +122,20 @@ class ScEnvironmentCard extends React.Component {
);
}
+ renderWarning(env) {
+ if (isAppStreamEnabled && !env.isAppStreamConfigured && env.state.canTerminate) {
+ return (
+
+ );
+ }
+
+ return null;
+ }
+
renderError(env) {
if (_.isEmpty(env.error)) return null;
diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/ScEnvironmentsList.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/ScEnvironmentsList.js
index 4f445a7ab0..8c737a0189 100644
--- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/ScEnvironmentsList.js
+++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/ScEnvironmentsList.js
@@ -112,7 +112,7 @@ class ScEnvironmentsList extends React.Component {
} else if (isStoreEmpty(store)) {
content = this.renderEmpty();
} else if (isStoreNotEmpty(store)) {
- content = this.renderMain(appStreamProjectIds);
+ content = this.renderMain();
} else {
content = null;
}
@@ -143,11 +143,10 @@ class ScEnvironmentsList extends React.Component {
);
}
- renderMain(appStreamProjectIds) {
+ renderMain() {
const store = this.envsStore;
const selectedFilter = this.selectedFilter;
- let list = store.filtered(selectedFilter);
- list = this.isAppStreamEnabled ? _.filter(list, env => _.includes(appStreamProjectIds, env.projectId)) : list;
+ const list = store.filtered(selectedFilter);
const isEmpty = _.isEmpty(list);
return (
diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentButtons.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentButtons.js
index 5452a3f428..f35725aa9d 100644
--- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentButtons.js
+++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentButtons.js
@@ -194,7 +194,11 @@ class ScEnvironmentButtons extends React.Component {
)}
- {canConnect && (
+ {/* Only let users connect to the environment if either of these conditions is true:
+ 1. AppStream is not enabled and environment can be connected to
+ 2. AppStream is enabled, environment is linked to an AppStream-configured account, and environment can be connected to
+ */}
+ {canConnect && (!isAppStreamEnabled || env.isAppStreamConfigured) && (