Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Apache License Version 2.0

Copyright (c) 2025 Salesforce, Inc.
Copyright (c) 2026 Salesforce, Inc.
All rights reserved.

Apache License
Expand Down
8 changes: 8 additions & 0 deletions command-snapshot.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@
],
"plugin": "@salesforce/plugin-orchestrator"
},
{
"alias": [],
"command": "orchestrator:app:decouple",
"flagAliases": [],
"flagChars": ["i", "o"],
"flags": ["api-version", "app-id", "flags-dir", "json", "target-org"],
"plugin": "@salesforce/plugin-orchestrator"
},
{
"alias": [],
"command": "orchestrator:app:delete",
Expand Down
105 changes: 105 additions & 0 deletions messages/appframework.decouple.app.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# summary

Decouple an app from its template.

# description

Decouple an existing app from its template, removing all references to the template from the app. This operation makes the app independent and no longer tied to template updates or changes. Once decoupled, the app maintains its current configuration but can be modified independently without affecting the original template.

You must specify the app to decouple using its unique ID. App IDs are guaranteed to be unique and work reliably with the decouple operation.

Use this command when you want to break the relationship between an app and its template, allowing for independent customization without template constraints. After decoupling, the app will no longer receive updates when the template is modified.

You must have the AppFrameworkManageApp user permission to decouple apps. The app must exist in the target org and be currently associated with a template.

# examples

- Decouple an app from its template:

<%= config.bin %> <%= command.id %> --target-org myOrg --app-id 1zAxx000000004rEAA

- Decouple an app using a specific API version:

<%= config.bin %> <%= command.id %> --target-org mySandbox --app-id 1zAxx000000004rEAA --api-version 60.0

- Find app IDs using the list command first:

<%= config.bin %> orchestrator app list --target-org myOrg

# flags.target-org.summary

Login username or alias for the target org.

# flags.target-org.description

The target org containing the app to decouple. You must have the AppFrameworkManageApp user permission in this org to decouple apps.

# flags.api-version.summary

Override the API version used for API requests.

# flags.api-version.description

Override the API version used for orchestrator API requests. Use this flag to specify a particular API version when the default version doesn't work with your org's configuration.

# flags.app-id.summary

ID of the app to decouple from its template.

# flags.app-id.description

The unique identifier of the app to decouple from its template. App IDs are guaranteed to be unique within the org and work reliably with the decouple operation. Use "sf orchestrator app list" to find app IDs.

# decouplingApp

Decoupling app from template...

# decoupleSuccess

Successfully decoupled app '%s' from its template.

# error.AppNotFound

App with ID '%s' not found.

# error.AppNotFound.Actions

- Verify the app ID is correct
- Use "sf orchestrator app list" to see available apps and their IDs
- Check your permissions to view apps
- Make sure you're connected to the correct org with --target-org

# error.CannotDecouple

The app '%s' cannot be decoupled from its template. This app may be a source template or doesn't meet the requirements for decoupling.

# error.CannotDecouple.Actions

- Check that this app is not the source template for other apps
- Verify that the app is currently associated with a template (has templateSourceId)
- Ensure you have the AppFrameworkManageApp permission
- Use "sf orchestrator app display --app-id <app-id>" to see app details
- Try decoupling from the app hub interface first

# error.InvalidOperation

The app cannot be decoupled from its template.

# error.InvalidOperation.Actions

- Verify that the app is currently associated with a template
- Check that you have permission to modify apps in the target org
- Ensure the app is not in a state that prevents decoupling
- Try using a different API version with --api-version

# error.GenericError

Failed to decouple app: %s

# error.GenericError.Actions

- Verify that you have permission to modify apps in the target org
- Check your authentication and org connection are valid
- Ensure the app exists and is accessible
- Try using a different API version with --api-version
- Contact your Salesforce admin if the issue persists
2 changes: 1 addition & 1 deletion src/commands/orchestrator/app/create.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2025, Salesforce, Inc.
* Copyright 2026, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
101 changes: 101 additions & 0 deletions src/commands/orchestrator/app/decouple.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright 2026, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License 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.
*/
import { Flags, SfCommand } from '@salesforce/sf-plugins-core';
import { Messages, SfError, Connection } from '@salesforce/core';
import AppFrameworkApp from '../../../utils/app/appframeworkapp.js';

Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('@salesforce/plugin-orchestrator', 'appframework.decouple.app');

export default class DecoupleApp extends SfCommand<string> {
public static readonly summary = messages.getMessage('summary');
public static readonly description = messages.getMessage('description');
public static readonly examples = messages.getMessages('examples');
public static readonly state = 'preview';

public static readonly flags = {
'target-org': Flags.requiredOrg({
summary: messages.getMessage('flags.target-org.summary'),
description: messages.getMessage('flags.target-org.description'),
required: true,
}),
'api-version': Flags.orgApiVersion({
summary: messages.getMessage('flags.api-version.summary'),
description: messages.getMessage('flags.api-version.description'),
default: '66.0',
}),
'app-id': Flags.string({
char: 'i',
summary: messages.getMessage('flags.app-id.summary'),
description: messages.getMessage('flags.app-id.description'),
required: true,
}),
};

public async run(): Promise<string> {
const { flags } = await this.parse(DecoupleApp);

try {
this.spinner.start(messages.getMessage('decouplingApp'));

type OrgType = { getConnection(apiVersion?: string): Connection };

const connection = (flags['target-org'] as OrgType).getConnection(flags['api-version']);
const appFrameworkApp = new AppFrameworkApp(connection);

// Use the app ID provided by the user
const appId = flags['app-id'];

// Call the decouple method
const result = await appFrameworkApp.decoupleApp(appId);

this.spinner.stop();

this.log(messages.getMessage('decoupleSuccess', [appId]));

return result;
} catch (error) {
this.spinner.stop();
const errorMsg = (error as Error).message;

if (errorMsg.includes('NOT_FOUND') || errorMsg.includes('404')) {
throw new SfError(
messages.getMessage('error.AppNotFound', [flags['app-id']]),
'AppNotFound',
messages.getMessages('error.AppNotFound.Actions')
);
} else if (errorMsg.includes('cannot be decoupled') || errorMsg.includes('INVALIDREQUEST')) {
throw new SfError(
messages.getMessage('error.CannotDecouple', [flags['app-id']]),
'CannotDecouple',
messages.getMessages('error.CannotDecouple.Actions')
);
} else if (errorMsg.includes('INVALID_OPERATION') || errorMsg.includes('400')) {
throw new SfError(
messages.getMessage('error.InvalidOperation'),
'InvalidOperation',
messages.getMessages('error.InvalidOperation.Actions')
);
} else {
throw new SfError(
messages.getMessage('error.GenericError', [errorMsg]),
'DecoupleError',
messages.getMessages('error.GenericError.Actions')
);
}
}
}
}
2 changes: 1 addition & 1 deletion src/commands/orchestrator/app/delete.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2025, Salesforce, Inc.
* Copyright 2026, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion src/commands/orchestrator/app/display.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2025, Salesforce, Inc.
* Copyright 2026, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion src/commands/orchestrator/app/list.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2025, Salesforce, Inc.
* Copyright 2026, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion src/commands/orchestrator/app/update.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2025, Salesforce, Inc.
* Copyright 2026, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion src/commands/orchestrator/app/upgrade.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2025, Salesforce, Inc.
* Copyright 2026, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion src/commands/orchestrator/rules/eval.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2025, Salesforce, Inc.
* Copyright 2026, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion src/commands/orchestrator/template/create.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2025, Salesforce, Inc.
* Copyright 2026, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion src/commands/orchestrator/template/delete.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2025, Salesforce, Inc.
* Copyright 2026, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion src/commands/orchestrator/template/display.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2025, Salesforce, Inc.
* Copyright 2026, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion src/commands/orchestrator/template/list.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2025, Salesforce, Inc.
* Copyright 2026, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion src/commands/orchestrator/template/update.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2025, Salesforce, Inc.
* Copyright 2026, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2025, Salesforce, Inc.
* Copyright 2026, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion src/utils/app/appDisplayUtil.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2025, Salesforce, Inc.
* Copyright 2026, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion src/utils/app/appListUtils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2025, Salesforce, Inc.
* Copyright 2026, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion src/utils/app/appTypes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2025, Salesforce, Inc.
* Copyright 2026, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Loading