From 490a6b5022452800493d44e35a2a0d8f5196c4bd Mon Sep 17 00:00:00 2001 From: Volker Scheuber Date: Mon, 5 Feb 2024 13:49:57 -0600 Subject: [PATCH] fixes #382 - Update NodeOps.ts with latest AM releases and node classifications --- src/ops/JourneyOps.test.ts | 2 +- src/ops/NodeOps.test.ts | 445 ++++++++++++++++++++ src/ops/NodeOps.ts | 183 +++++++- src/test/snapshots/ops/NodeOps.test.js.snap | 148 +++++++ 4 files changed, 761 insertions(+), 17 deletions(-) create mode 100644 src/ops/NodeOps.test.ts create mode 100644 src/test/snapshots/ops/NodeOps.test.js.snap diff --git a/src/ops/JourneyOps.test.ts b/src/ops/JourneyOps.test.ts index 712aaa013..a3dc3c098 100644 --- a/src/ops/JourneyOps.test.ts +++ b/src/ops/JourneyOps.test.ts @@ -198,7 +198,7 @@ describe('JourneyOps', () => { state }); //Ignore the _rev field - serverTree._rev = journey.tree._rev; + if (serverTree) serverTree._rev = journey.tree._rev; expect(serverTree).toStrictEqual(journey.tree); expect(tree).toMatchSnapshot(journey.tree); }); diff --git a/src/ops/NodeOps.test.ts b/src/ops/NodeOps.test.ts new file mode 100644 index 000000000..3dcfc4b34 --- /dev/null +++ b/src/ops/NodeOps.test.ts @@ -0,0 +1,445 @@ +/** + * To record and update snapshots, you must perform 3 steps in order: + * + * 1. Record API responses & update snapshots + * + * This step breaks down into 4 phases: + * + * Phase 1: Record Non-destructive tests + * Phase 2: Record Group 1 of DESTRUCTIVE tests - Deletes by ID + * Phase 3: Record Group 2 of DESTRUCTIVE tests - Deletes by tag + * Phase 4: Record Group 3 of DESTRUCTIVE tests - Delete all + * + * Because destructive tests interfere with the recording of non-destructive + * tests and also interfere among themselves, they have to be run in groups + * of non-interfering tests. + * + * To record and update snapshots, you must call the test:record + * script and override all the connection state variables required + * to connect to the env to record from and also indicate the phase: + * + * FRODO_DEBUG=1 FRODO_RECORD_PHASE=1 FRODO_HOST=frodo-dev npm run test:record NodeOps + * + * THESE TESTS ARE DESTRUCTIVE!!! DO NOT RUN AGAINST AN ENV WITH ACTIVE JOURNEYS!!! + * + * FRODO_DEBUG=1 FRODO_RECORD_PHASE=2 FRODO_HOST=frodo-dev npm run test:record NodeOps + * FRODO_DEBUG=1 FRODO_RECORD_PHASE=3 FRODO_HOST=frodo-dev npm run test:record NodeOps + * FRODO_DEBUG=1 FRODO_RECORD_PHASE=4 FRODO_HOST=frodo-dev npm run test:record NodeOps + * + * The above command assumes that you have a connection profile for + * 'frodo-dev' on your development machine. + * + * 2. Update CJS snapshots + * + * After recording, the ESM snapshots will already be updated as that happens + * in one go, but you must manually update the CJS snapshots by running: + * + * FRODO_DEBUG=1 npm run test:update NodeOps + * + * 3. Test your changes + * + * If 1 and 2 didn't produce any errors, you are ready to run the tests in + * replay mode and make sure they all succeed as well: + * + * npm run test:only NodeOps + * + * Note: FRODO_DEBUG=1 is optional and enables debug logging for some output + * in case things don't function as expected + */ +import { frodo, state } from '../index'; +import * as NodeOps from './NodeOps'; +import { autoSetupPolly, filterRecording } from '../utils/AutoSetupPolly'; +import Constants from '../shared/Constants'; + +const ctx = autoSetupPolly(); +const stateCloud750 = frodo.createInstance({ + amVersion: '7.5.0', + deploymentType: Constants.CLOUD_DEPLOYMENT_TYPE_KEY, +}).state; + +stateCloud750.setDeploymentType(Constants.CLOUD_DEPLOYMENT_TYPE_KEY); + +describe('NodeOps', () => { + // in recording mode, setup test data before recording + beforeAll(async () => { + if (process.env.FRODO_POLLY_MODE === 'record') { + // stage test data + } + }); + // in recording mode, remove test data after recording + afterAll(async () => { + if (process.env.FRODO_POLLY_MODE === 'record') { + // clean-up test data + } + }); + beforeEach(async () => { + if (process.env.FRODO_POLLY_MODE === 'record') { + ctx.polly.server.any().on('beforePersist', (_req, recording) => { + filterRecording(recording); + }); + } + }); + // Phase 1 + if ( + !process.env.FRODO_POLLY_MODE || + (process.env.FRODO_POLLY_MODE === 'record' && + process.env.FRODO_RECORD_PHASE === '1') + ) { + /* + TODO: Create tests for the following functions + + readNodeTypes(): Promise; + readNodes(): Promise; + readNodesByType(nodeType: string): Promise; + readNode(nodeId: string, nodeType: string): Promise; + createNode(nodeType: string, nodeData: NodeSkeleton): Promise; + updateNode( + nodeId: string, + nodeType: string, + nodeData: NodeSkeleton + ): Promise; + deleteNode(nodeId: string, nodeType: string): Promise; + findOrphanedNodes(): Promise; + removeOrphanedNodes(orphanedNodes: NodeSkeleton[]): Promise; + */ + describe('isPremiumNode()', () => { + test('0: Method is implemented', async () => { + expect(NodeOps.isPremiumNode).toBeDefined(); + }); + + test('1: PageNode is not a premium node', async () => { + expect(NodeOps.isPremiumNode('PageNode')).toMatchSnapshot(); + }); + + test('2: AutonomousAccessDecisionNode is a premium node', async () => { + expect( + NodeOps.isPremiumNode('AutonomousAccessDecisionNode') + ).toMatchSnapshot(); + }); + + test('3: AutonomousAccessResultNode is a premium node', async () => { + expect( + NodeOps.isPremiumNode('AutonomousAccessResultNode') + ).toMatchSnapshot(); + }); + + test('4: AutonomousAccessSignalNode is a premium node', async () => { + expect( + NodeOps.isPremiumNode('AutonomousAccessSignalNode') + ).toMatchSnapshot(); + }); + }); + + describe('isCloudOnlyNode()', () => { + test('0: Method is implemented', async () => { + expect(NodeOps.isCloudOnlyNode).toBeDefined(); + }); + + test('1: LdapDecisionNode is not a cloud-only node', async () => { + expect(NodeOps.isCloudOnlyNode('LdapDecisionNode')).toMatchSnapshot(); + }); + + test('2: AutonomousAccessDecisionNode is a cloud-only node', async () => { + expect( + NodeOps.isCloudOnlyNode('AutonomousAccessDecisionNode') + ).toMatchSnapshot(); + }); + + test('3: AutonomousAccessResultNode is a cloud-only node', async () => { + expect( + NodeOps.isCloudOnlyNode('AutonomousAccessResultNode') + ).toMatchSnapshot(); + }); + + test('4: AutonomousAccessSignalNode is a cloud-only node', async () => { + expect( + NodeOps.isCloudOnlyNode('AutonomousAccessSignalNode') + ).toMatchSnapshot(); + }); + + test('5: IdentityStoreDecisionNode is a cloud-only node', async () => { + expect( + NodeOps.isCloudOnlyNode('IdentityStoreDecisionNode') + ).toMatchSnapshot(); + }); + }); + + describe('isCloudExcludedNode()', () => { + test('0: Method is implemented', async () => { + expect(NodeOps.isCloudExcludedNode).toBeDefined(); + }); + + test('1: LdapDecisionNode is not a cloud-excluded node', async () => { + expect( + NodeOps.isCloudExcludedNode({ + nodeType: 'LdapDecisionNode', + state: stateCloud750, + }) + ).toMatchSnapshot(); + }); + test('2: CreatePasswordNode is a cloud-excluded node', async () => { + expect( + NodeOps.isCloudExcludedNode({ + nodeType: 'CreatePasswordNode', + state: stateCloud750, + }) + ).toMatchSnapshot(); + }); + + test('3: ProvisionDynamicAccountNode is a cloud-excluded node', async () => { + expect( + NodeOps.isCloudExcludedNode({ + nodeType: 'ProvisionDynamicAccountNode', + state: stateCloud750, + }) + ).toMatchSnapshot(); + }); + + test('4: ProvisionIdmAccountNode is a cloud-excluded node', async () => { + expect( + NodeOps.isCloudExcludedNode({ + nodeType: 'ProvisionIdmAccountNode', + state: stateCloud750, + }) + ).toMatchSnapshot(); + }); + + test('5: SocialFacebookNode is a cloud-excluded node', async () => { + expect( + NodeOps.isCloudExcludedNode({ + nodeType: 'SocialFacebookNode', + state: stateCloud750, + }) + ).toMatchSnapshot(); + }); + + test('6: SocialGoogleNode is a cloud-excluded node', async () => { + expect( + NodeOps.isCloudExcludedNode({ + nodeType: 'SocialGoogleNode', + state: stateCloud750, + }) + ).toMatchSnapshot(); + }); + + test('7: SocialNode is a cloud-excluded node', async () => { + expect( + NodeOps.isCloudExcludedNode({ + nodeType: 'SocialNode', + state: stateCloud750, + }) + ).toMatchSnapshot(); + }); + + test('8: SocialOAuthIgnoreProfileNode is a cloud-excluded node', async () => { + expect( + NodeOps.isCloudExcludedNode({ + nodeType: 'SocialOAuthIgnoreProfileNode', + state: stateCloud750, + }) + ).toMatchSnapshot(); + }); + + test('9: SocialOpenIdConnectNode is a cloud-excluded node', async () => { + expect( + NodeOps.isCloudExcludedNode({ + nodeType: 'SocialOpenIdConnectNode', + state: stateCloud750, + }) + ).toMatchSnapshot(); + }); + }); + + describe('isDeprecatedNode()', () => { + test('0: Method is implemented', async () => { + expect(NodeOps.isDeprecatedNode).toBeDefined(); + }); + + test('1: PageNode is not a deprecated node', async () => { + expect( + NodeOps.isDeprecatedNode({ + nodeType: 'PageNode', + state: stateCloud750, + }) + ).toMatchSnapshot(); + }); + + test('2: SocialProviderHandlerNode is a deprecated node', async () => { + expect( + NodeOps.isDeprecatedNode({ + nodeType: 'SocialProviderHandlerNode', + state: stateCloud750, + }) + ).toMatchSnapshot(); + }); + + test('3: product-ReCaptchaNode is a deprecated node', async () => { + expect( + NodeOps.isDeprecatedNode({ + nodeType: 'product-ReCaptchaNode', + state: stateCloud750, + }) + ).toMatchSnapshot(); + }); + }); + + describe('isCustomNode()', () => { + test('0: Method is implemented', async () => { + expect(NodeOps.isCustomNode).toBeDefined(); + }); + + test('1: PageNode is not a custom node', async () => { + expect( + NodeOps.isCustomNode({ nodeType: 'PageNode', state: stateCloud750 }) + ).toMatchSnapshot(); + }); + + test('2: CustomNode is a custom node', async () => { + expect( + NodeOps.isCustomNode({ + nodeType: 'CustomNode', + state: stateCloud750, + }) + ).toMatchSnapshot(); + }); + }); + + describe('getNodeClassification()', () => { + test('0: Method is implemented', async () => { + expect(NodeOps.getNodeClassification).toBeDefined(); + }); + + // standard + test('1: Classification of "PageNode" is "standard"', async () => { + const classification = NodeOps.getNodeClassification({ + nodeType: 'PageNode', + state: stateCloud750, + }); + expect(classification).toMatchSnapshot(); + }); + + // cloud, premium + test('2: Classification of "AutonomousAccessSignalNode" is "cloud, premium"', async () => { + const classification = NodeOps.getNodeClassification({ + nodeType: 'AutonomousAccessSignalNode', + state: stateCloud750, + }); + expect(classification).toMatchSnapshot(); + }); + test('3: Classification of "AutonomousAccessDecisionNode" is "cloud, premium"', async () => { + const classification = NodeOps.getNodeClassification({ + nodeType: 'AutonomousAccessDecisionNode', + state: stateCloud750, + }); + expect(classification).toMatchSnapshot(); + }); + test('4: Classification of "AutonomousAccessResultNode" is "cloud, premium"', async () => { + const classification = NodeOps.getNodeClassification({ + nodeType: 'AutonomousAccessResultNode', + state: stateCloud750, + }); + expect(classification).toMatchSnapshot(); + }); + + // cloud + test('5: Classification of "IdentityStoreDecisionNode" is "cloud"', async () => { + const classification = NodeOps.getNodeClassification({ + nodeType: 'IdentityStoreDecisionNode', + state: stateCloud750, + }); + expect(classification).toMatchSnapshot(); + }); + + // excluded + test('6: Classification of "CreatePasswordNode" is "excluded"', async () => { + const classification = NodeOps.getNodeClassification({ + nodeType: 'CreatePasswordNode', + state: stateCloud750, + }); + expect(classification).toMatchSnapshot(); + }); + + test('7: Classification of "ProvisionDynamicAccountNode" is "excluded"', async () => { + const classification = NodeOps.getNodeClassification({ + nodeType: 'ProvisionDynamicAccountNode', + state: stateCloud750, + }); + expect(classification).toMatchSnapshot(); + }); + + test('8: Classification of "ProvisionIdmAccountNode" is "excluded"', async () => { + const classification = NodeOps.getNodeClassification({ + nodeType: 'ProvisionIdmAccountNode', + state: stateCloud750, + }); + expect(classification).toMatchSnapshot(); + }); + + test('9: Classification of "SocialFacebookNode" is "excluded"', async () => { + const classification = NodeOps.getNodeClassification({ + nodeType: 'SocialFacebookNode', + state: stateCloud750, + }); + expect(classification).toMatchSnapshot(); + }); + + test('10: Classification of "SocialGoogleNode" is "excluded"', async () => { + const classification = NodeOps.getNodeClassification({ + nodeType: 'SocialGoogleNode', + state: stateCloud750, + }); + expect(classification).toMatchSnapshot(); + }); + + test('11: Classification of "SocialNode" is "excluded"', async () => { + const classification = NodeOps.getNodeClassification({ + nodeType: 'SocialNode', + state: stateCloud750, + }); + expect(classification).toMatchSnapshot(); + }); + + test('12: Classification of "SocialOAuthIgnoreProfileNode" is "excluded"', async () => { + const classification = NodeOps.getNodeClassification({ + nodeType: 'SocialOAuthIgnoreProfileNode', + state: stateCloud750, + }); + expect(classification).toMatchSnapshot(); + }); + + test('13: Classification of "SocialOpenIdConnectNode" is "excluded"', async () => { + const classification = NodeOps.getNodeClassification({ + nodeType: 'SocialOpenIdConnectNode', + state: stateCloud750, + }); + expect(classification).toMatchSnapshot(); + }); + + // deprecated + test('14: Classification of "SocialProviderHandlerNode" is "standard, deprecated"', async () => { + const classification = NodeOps.getNodeClassification({ + nodeType: 'SocialProviderHandlerNode', + state: stateCloud750, + }); + expect(classification).toMatchSnapshot(); + }); + + test('15: Classification of "product-ReCaptchaNode" is "standard, deprecated"', async () => { + const classification = NodeOps.getNodeClassification({ + nodeType: 'product-ReCaptchaNode', + state: stateCloud750, + }); + expect(classification).toMatchSnapshot(); + }); + + // custom + test('16: Classification of "CustomNode" is "custom"', async () => { + const classification = NodeOps.getNodeClassification({ + nodeType: 'CustomNode', + state: stateCloud750, + }); + expect(classification).toMatchSnapshot(); + }); + }); + } +}); diff --git a/src/ops/NodeOps.ts b/src/ops/NodeOps.ts index 497189260..6cf1abad0 100644 --- a/src/ops/NodeOps.ts +++ b/src/ops/NodeOps.ts @@ -10,6 +10,7 @@ import { putNode as _putNode, } from '../api/NodeApi'; import { getTrees } from '../api/TreeApi'; +import Constants from '../shared/Constants'; import { State } from '../shared/State'; import { createProgressIndicator, @@ -80,17 +81,29 @@ export type Node = { */ removeOrphanedNodes(orphanedNodes: NodeSkeleton[]): Promise; /** - * Analyze if a node is a premium node. + * Analyze if a node type is premium. * @param {string} nodeType Node type * @returns {boolean} True if the node type is premium, false otherwise. */ isPremiumNode(nodeType: string): boolean; /** - * Analyze if a node is a cloud-only node. + * Analyze if a node type is a cloud-only node. * @param {string} nodeType Node type * @returns {boolean} True if the node type is cloud-only, false otherwise. */ isCloudOnlyNode(nodeType: string): boolean; + /** + * Analyze if a node type is a cloud-excluded node. Cloud excluded nodes are OOTB nodes in self-hosted AM deployments but have been excluded in cloud. + * @param {string} nodeType node type. + * @returns {boolean} True if node type is cloud-excluded, false otherwise. + */ + isCloudExcludedNode(nodeType: string): boolean; + /** + * Analyze if a node type has been deprecated + * @param {string} nodeType node type. + * @returns {boolean} True if node type is deprecated, false otherwise. + */ + isDeprecatedNode(nodeType: string): boolean; /** * Analyze if a node is custom. * @param {string} nodeType Node type @@ -152,6 +165,12 @@ export default (state: State): Node => { isCloudOnlyNode(nodeType: string): boolean { return isCloudOnlyNode(nodeType); }, + isCloudExcludedNode(nodeType: string): boolean { + return isCloudExcludedNode({ nodeType, state }); + }, + isDeprecatedNode(nodeType: string): boolean { + return isDeprecatedNode({ nodeType, state }); + }, isCustomNode(nodeType: string): boolean { return isCustomNode({ nodeType, state }); }, @@ -165,13 +184,17 @@ export type NodeClassificationType = | 'standard' | 'custom' | 'cloud' - | 'premium'; + | 'excluded' + | 'premium' + | 'deprecated'; export enum NodeClassification { STANDARD = 'standard', CUSTOM = 'custom', CLOUD = 'cloud', + EXCLUDED = 'excluded', PREMIUM = 'premium', + DEPRECATED = 'deprecated', } const containerNodes = ['PageNode', 'CustomPageNode']; @@ -526,6 +549,13 @@ const OOTB_NODE_TYPES_7 = [ 'PatchObjectNode', 'PersistentCookieDecisionNode', 'PollingWaitNode', + 'product-CertificateCollectorNode', + 'product-CertificateUserExtractorNode', + 'product-CertificateValidationNode', + 'product-KerberosNode', + 'product-ReCaptchaNode', + 'product-Saml2Node', + 'product-WriteFederationInformationNode', 'ProfileCompletenessDecisionNode', 'ProvisionDynamicAccountNode', 'ProvisionIdmAccountNode', @@ -552,9 +582,9 @@ const OOTB_NODE_TYPES_7 = [ 'SocialOpenIdConnectNode', 'SocialProviderHandlerNode', 'TermsAndConditionsDecisionNode', - 'TimeSinceDecisionNode', 'TimerStartNode', 'TimerStopNode', + 'TimeSinceDecisionNode', 'UsernameCollectorNode', 'ValidatedPasswordNode', 'ValidatedUsernameNode', @@ -562,33 +592,61 @@ const OOTB_NODE_TYPES_7 = [ 'WebAuthnDeviceStorageNode', 'WebAuthnRegistrationNode', 'ZeroPageLoginNode', - 'product-CertificateCollectorNode', - 'product-CertificateUserExtractorNode', - 'product-CertificateValidationNode', - 'product-KerberosNode', - 'product-ReCaptchaNode', - 'product-Saml2Node', - 'product-WriteFederationInformationNode', ]; +const DEPRECATED_NODE_TYPES_7 = []; + const OOTB_NODE_TYPES_7_1 = [ - 'PushRegistrationNode', 'GetAuthenticatorAppNode', 'MultiFactorRegistrationOptionsNode', 'OptOutMultiFactorAuthenticationNode', + 'PushRegistrationNode', ].concat(OOTB_NODE_TYPES_7); +const DEPRECATED_NODE_TYPES_7_1 = [].concat(DEPRECATED_NODE_TYPES_7); + const OOTB_NODE_TYPES_7_2 = [ + 'ConfigProviderNode', + 'DebugNode', 'OathRegistrationNode', 'OathTokenVerifierNode', 'PassthroughAuthenticationNode', - 'ConfigProviderNode', - 'DebugNode', + 'product-CaptchaNode', + 'PushWaitNode', + 'SetCustomCookieNode', ].concat(OOTB_NODE_TYPES_7_1); -const OOTB_NODE_TYPES_7_3 = [].concat(OOTB_NODE_TYPES_7_2); +const DEPRECATED_NODE_TYPES_7_2 = ['product-ReCaptchaNode'].concat( + DEPRECATED_NODE_TYPES_7_1 +); + +const OOTB_NODE_TYPES_7_3 = [ + 'CombinedMultiFactorRegistrationNode', + 'OathDeviceStorageNode', + 'OidcNode', +].concat(OOTB_NODE_TYPES_7_2); + +const DEPRECATED_NODE_TYPES_7_3 = [].concat(DEPRECATED_NODE_TYPES_7_2); + +const OOTB_NODE_TYPES_7_4 = ['QueryParameterNode'].concat(OOTB_NODE_TYPES_7_3); -const OOTB_NODE_TYPES_7_4 = [].concat(OOTB_NODE_TYPES_7_3); +const DEPRECATED_NODE_TYPES_7_4 = [].concat(DEPRECATED_NODE_TYPES_7_3); + +const OOTB_NODE_TYPES_7_5 = [ + 'DeviceBindingNode', + 'DeviceBindingStorageNode', + 'DeviceSigningVerifierNode', +].concat(OOTB_NODE_TYPES_7_4); + +const DEPRECATED_NODE_TYPES_7_5 = ['SocialProviderHandlerNode'].concat( + DEPRECATED_NODE_TYPES_7_4 +); + +// move above 7 release nodes once 8 becomes a release version +const OOTB_NODE_TYPES_8 = [].concat(OOTB_NODE_TYPES_7_5); + +// move above 7 release nodes once 8 becomes a release version +const DEPRECATED_NODE_TYPES_8 = [].concat(DEPRECATED_NODE_TYPES_7_5); const OOTB_NODE_TYPES_6_5 = [ 'AbstractSocialAuthLoginNode', @@ -694,6 +752,17 @@ const OOTB_NODE_TYPES_6 = [ 'ZeroPageLoginNode', ]; +const CLOUD_EXCLUDED_NODE_TYPES = [ + 'CreatePasswordNode', + 'ProvisionDynamicAccountNode', + 'ProvisionIdmAccountNode', + 'SocialFacebookNode', + 'SocialGoogleNode', + 'SocialNode', + 'SocialOAuthIgnoreProfileNode', + 'SocialOpenIdConnectNode', +]; + const CLOUD_ONLY_NODE_TYPES = [ 'IdentityStoreDecisionNode', 'AutonomousAccessSignalNode', @@ -725,6 +794,72 @@ export function isCloudOnlyNode(nodeType: string): boolean { return CLOUD_ONLY_NODE_TYPES.includes(nodeType); } +/** + * Analyze if a node is a cloud-excluded node. Cloud excluded nodes are OOTB nodes in self-hosted AM deployments but have been excluded in cloud. + * @param {{string, State}} param0 object containing node type and state. + * @returns {boolean} True if node type is cloud-excluded, false otherwise. + */ +export function isCloudExcludedNode({ + nodeType, + state, +}: { + nodeType: string; + state: State; +}): boolean { + return ( + state.getDeploymentType() === Constants.CLOUD_DEPLOYMENT_TYPE_KEY && + CLOUD_EXCLUDED_NODE_TYPES.includes(nodeType) + ); +} + +/** + * Analyze if node has been deprecated + * @param {{string, State}} param0 object containing node type and state. + * @returns {boolean} True if node type is deprecated, false otherwise. + */ +export function isDeprecatedNode({ + nodeType, + state, +}: { + nodeType: string; + state: State; +}): boolean { + let deprecatedNodeTypes = []; + switch (state.getAmVersion()) { + case '8.0.0': + deprecatedNodeTypes = DEPRECATED_NODE_TYPES_8.slice(0); + break; + case '7.1.0': + case '7.1.1': + case '7.1.2': + case '7.1.3': + case '7.1.4': + deprecatedNodeTypes = DEPRECATED_NODE_TYPES_7_1.slice(0); + break; + case '7.2.0': + case '7.2.1': + deprecatedNodeTypes = DEPRECATED_NODE_TYPES_7_2.slice(0); + break; + case '7.3.0': + deprecatedNodeTypes = DEPRECATED_NODE_TYPES_7_3.slice(0); + break; + case '7.4.0': + deprecatedNodeTypes = DEPRECATED_NODE_TYPES_7_4.slice(0); + break; + case '7.5.0': + deprecatedNodeTypes = DEPRECATED_NODE_TYPES_7_5.slice(0); + break; + case '7.0.0': + case '7.0.1': + case '7.0.2': + deprecatedNodeTypes = DEPRECATED_NODE_TYPES_7.slice(0); + break; + default: + return false; + } + return deprecatedNodeTypes.includes(nodeType); +} + /** * Analyze if a node is custom. * @param {string} nodeType Node type @@ -739,10 +874,18 @@ export function isCustomNode({ }): boolean { let ootbNodeTypes = []; switch (state.getAmVersion()) { + case '8.0.0': + ootbNodeTypes = OOTB_NODE_TYPES_8.slice(0); + break; case '7.1.0': + case '7.1.1': + case '7.1.2': + case '7.1.3': + case '7.1.4': ootbNodeTypes = OOTB_NODE_TYPES_7_1.slice(0); break; case '7.2.0': + case '7.2.1': ootbNodeTypes = OOTB_NODE_TYPES_7_2.slice(0); break; case '7.3.0': @@ -751,6 +894,9 @@ export function isCustomNode({ case '7.4.0': ootbNodeTypes = OOTB_NODE_TYPES_7_4.slice(0); break; + case '7.5.0': + ootbNodeTypes = OOTB_NODE_TYPES_7_5.slice(0); + break; case '7.0.0': case '7.0.1': case '7.0.2': @@ -805,13 +951,18 @@ export function getNodeClassification({ const premium = isPremiumNode(nodeType); const custom = isCustomNode({ nodeType, state }); const cloud = isCloudOnlyNode(nodeType); + const excluded = isCloudExcludedNode({ nodeType, state }); + const deprecated = isDeprecatedNode({ nodeType, state }); if (custom) { classifications.push(NodeClassification.CUSTOM); } else if (cloud) { classifications.push(NodeClassification.CLOUD); + } else if (excluded) { + classifications.push(NodeClassification.EXCLUDED); } else { classifications.push(NodeClassification.STANDARD); } if (premium) classifications.push(NodeClassification.PREMIUM); + if (deprecated) classifications.push(NodeClassification.DEPRECATED); return classifications; } diff --git a/src/test/snapshots/ops/NodeOps.test.js.snap b/src/test/snapshots/ops/NodeOps.test.js.snap new file mode 100644 index 000000000..f19687d69 --- /dev/null +++ b/src/test/snapshots/ops/NodeOps.test.js.snap @@ -0,0 +1,148 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`NodeOps getNodeClassification() 1: Classification of "PageNode" is "standard" 1`] = ` +[ + "standard", +] +`; + +exports[`NodeOps getNodeClassification() 2: Classification of "AutonomousAccessSignalNode" is "cloud, premium" 1`] = ` +[ + "cloud", + "premium", +] +`; + +exports[`NodeOps getNodeClassification() 3: Classification of "AutonomousAccessDecisionNode" is "cloud, premium" 1`] = ` +[ + "cloud", + "premium", +] +`; + +exports[`NodeOps getNodeClassification() 4: Classification of "AutonomousAccessResultNode" is "cloud, premium" 1`] = ` +[ + "cloud", + "premium", +] +`; + +exports[`NodeOps getNodeClassification() 5: Classification of "IdentityStoreDecisionNode" is "cloud" 1`] = ` +[ + "cloud", +] +`; + +exports[`NodeOps getNodeClassification() 6: Classification of "CreatePasswordNode" is "excluded" 1`] = ` +[ + "excluded", +] +`; + +exports[`NodeOps getNodeClassification() 7: Classification of "ProvisionDynamicAccountNode" is "excluded" 1`] = ` +[ + "excluded", +] +`; + +exports[`NodeOps getNodeClassification() 8: Classification of "ProvisionIdmAccountNode" is "excluded" 1`] = ` +[ + "excluded", +] +`; + +exports[`NodeOps getNodeClassification() 9: Classification of "SocialFacebookNode" is "excluded" 1`] = ` +[ + "excluded", +] +`; + +exports[`NodeOps getNodeClassification() 10: Classification of "SocialGoogleNode" is "excluded" 1`] = ` +[ + "excluded", +] +`; + +exports[`NodeOps getNodeClassification() 11: Classification of "SocialNode" is "excluded" 1`] = ` +[ + "excluded", +] +`; + +exports[`NodeOps getNodeClassification() 12: Classification of "SocialOAuthIgnoreProfileNode" is "excluded" 1`] = ` +[ + "excluded", +] +`; + +exports[`NodeOps getNodeClassification() 13: Classification of "SocialOpenIdConnectNode" is "excluded" 1`] = ` +[ + "excluded", +] +`; + +exports[`NodeOps getNodeClassification() 14: Classification of "SocialProviderHandlerNode" is "standard, deprecated" 1`] = ` +[ + "standard", + "deprecated", +] +`; + +exports[`NodeOps getNodeClassification() 15: Classification of "product-ReCaptchaNode" is "standard, deprecated" 1`] = ` +[ + "standard", + "deprecated", +] +`; + +exports[`NodeOps getNodeClassification() 16: Classification of "CustomNode" is "custom" 1`] = ` +[ + "custom", +] +`; + +exports[`NodeOps isCloudExcludedNode() 1: LdapDecisionNode is not a cloud-excluded node 1`] = `false`; + +exports[`NodeOps isCloudExcludedNode() 2: CreatePasswordNode is a cloud-excluded node 1`] = `true`; + +exports[`NodeOps isCloudExcludedNode() 3: ProvisionDynamicAccountNode is a cloud-excluded node 1`] = `true`; + +exports[`NodeOps isCloudExcludedNode() 4: ProvisionIdmAccountNode is a cloud-excluded node 1`] = `true`; + +exports[`NodeOps isCloudExcludedNode() 5: SocialFacebookNode is a cloud-excluded node 1`] = `true`; + +exports[`NodeOps isCloudExcludedNode() 6: SocialGoogleNode is a cloud-excluded node 1`] = `true`; + +exports[`NodeOps isCloudExcludedNode() 7: SocialNode is a cloud-excluded node 1`] = `true`; + +exports[`NodeOps isCloudExcludedNode() 8: SocialOAuthIgnoreProfileNode is a cloud-excluded node 1`] = `true`; + +exports[`NodeOps isCloudExcludedNode() 9: SocialOpenIdConnectNode is a cloud-excluded node 1`] = `true`; + +exports[`NodeOps isCloudOnlyNode() 1: LdapDecisionNode is not a cloud-only node 1`] = `false`; + +exports[`NodeOps isCloudOnlyNode() 2: AutonomousAccessDecisionNode is a cloud-only node 1`] = `true`; + +exports[`NodeOps isCloudOnlyNode() 3: AutonomousAccessResultNode is a cloud-only node 1`] = `true`; + +exports[`NodeOps isCloudOnlyNode() 4: AutonomousAccessSignalNode is a cloud-only node 1`] = `true`; + +exports[`NodeOps isCloudOnlyNode() 5: IdentityStoreDecisionNode is a cloud-only node 1`] = `true`; + +exports[`NodeOps isCustomNode() 1: PageNode is not a custom node 1`] = `false`; + +exports[`NodeOps isCustomNode() 2: CustomNode is a custom node 1`] = `true`; + +exports[`NodeOps isDeprecatedNode() 1: PageNode is not a deprecated node 1`] = `false`; + +exports[`NodeOps isDeprecatedNode() 2: SocialProviderHandlerNode is a deprecated node 1`] = `true`; + +exports[`NodeOps isDeprecatedNode() 3: product-ReCaptchaNode is a deprecated node 1`] = `true`; + +exports[`NodeOps isPremiumNode() 1: PageNode is not a premium node 1`] = `false`; + +exports[`NodeOps isPremiumNode() 2: AutonomousAccessDecisionNode is a premium node 1`] = `true`; + +exports[`NodeOps isPremiumNode() 3: AutonomousAccessResultNode is a premium node 1`] = `true`; + +exports[`NodeOps isPremiumNode() 4: AutonomousAccessSignalNode is a premium node 1`] = `true`;