Skip to content

Commit 14bd995

Browse files
committed
feat(slack/onboard-llmo): add IMS org onboarding option
1 parent f9ebbbd commit 14bd995

File tree

7 files changed

+635
-66
lines changed

7 files changed

+635
-66
lines changed

package-lock.json

Lines changed: 0 additions & 59 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/controllers/slack.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export function initSlackBot(lambdaContext, App) {
8080
app.view('onboard_site_modal', actions.onboardSiteModal(lambdaContext));
8181
app.view('preflight_config_modal', actions.preflight_config_modal(lambdaContext));
8282
app.view('onboard_llmo_modal', actions.onboardLLMOModal(lambdaContext));
83+
app.view('onboard_llmo_org_modal', actions.onboardLLMOOrgModal(lambdaContext));
8384
app.view('update_ims_org_modal', actions.updateIMSOrgModal(lambdaContext));
8485

8586
return app;

src/support/slack/actions/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import {
2121
addEntitlementsAction,
2222
updateOrgAction,
2323
updateIMSOrgModal,
24+
startLLMOOrgOnboarding,
25+
onboardLLMOOrgModal,
2426
} from './onboard-llmo-modal.js';
2527
import { onboardSiteModal, startOnboarding } from './onboard-modal.js';
2628
import { preflightConfigModal } from './preflight-config-modal.js';
@@ -34,9 +36,11 @@ const actions = {
3436
rejectOrg,
3537
onboardSiteModal,
3638
onboardLLMOModal,
39+
onboardLLMOOrgModal,
3740
updateIMSOrgModal,
3841
start_onboarding: startOnboarding,
3942
start_llmo_onboarding: startLLMOOnboarding,
43+
start_llmo_org_onboarding: startLLMOOrgOnboarding,
4044
preflight_config_modal: preflightConfigModal,
4145
open_preflight_config: openPreflightConfig,
4246
add_entitlements_action: addEntitlementsAction,

src/support/slack/actions/onboard-llmo-modal.js

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -940,3 +940,197 @@ export function updateIMSOrgModal(lambdaContext) {
940940
}
941941
};
942942
}
943+
944+
/* Handles "Start Onboarding" button click for IMS org onboarding */
945+
export function startLLMOOrgOnboarding(lambdaContext) {
946+
const { log } = lambdaContext;
947+
948+
return async ({
949+
ack, body, client, respond,
950+
}) => {
951+
try {
952+
await ack();
953+
954+
const { user } = body;
955+
956+
await respond({
957+
text: `:gear: ${user.name} started the IMS org onboarding process...`,
958+
replace_original: true,
959+
});
960+
961+
const originalChannel = body.channel?.id;
962+
const originalThreadTs = body.message?.thread_ts || body.message?.ts;
963+
964+
await client.views.open({
965+
trigger_id: body.trigger_id,
966+
view: {
967+
type: 'modal',
968+
callback_id: 'onboard_llmo_org_modal',
969+
private_metadata: JSON.stringify({
970+
originalChannel,
971+
originalThreadTs,
972+
}),
973+
title: {
974+
type: 'plain_text',
975+
text: 'Onboard IMS Org',
976+
},
977+
submit: {
978+
type: 'plain_text',
979+
text: 'Start Onboarding',
980+
},
981+
close: {
982+
type: 'plain_text',
983+
text: 'Cancel',
984+
},
985+
blocks: [
986+
{
987+
type: 'section',
988+
text: {
989+
type: 'mrkdwn',
990+
text: ':rocket: *LLMO IMS Org Onboarding*\n\nProvide the IMS Organization ID to onboard for LLMO.',
991+
},
992+
},
993+
{
994+
type: 'input',
995+
block_id: 'ims_org_input',
996+
element: {
997+
type: 'plain_text_input',
998+
action_id: 'ims_org_id',
999+
placeholder: {
1000+
type: 'plain_text',
1001+
text: 'ABC123@AdobeOrg',
1002+
},
1003+
},
1004+
label: {
1005+
type: 'plain_text',
1006+
text: 'IMS Organization ID',
1007+
},
1008+
},
1009+
],
1010+
},
1011+
});
1012+
1013+
log.debug(`User ${user.id} started IMS org onboarding process.`);
1014+
} catch (error) {
1015+
log.error('Error starting IMS org onboarding:', error);
1016+
await postErrorMessage(respond, error);
1017+
}
1018+
};
1019+
}
1020+
1021+
/* Handles IMS org onboarding modal submission */
1022+
export function onboardLLMOOrgModal(lambdaContext) {
1023+
const { log, dataAccess, imsClient } = lambdaContext;
1024+
1025+
return async ({ ack, body, client }) => {
1026+
try {
1027+
const { user, view } = body;
1028+
const { values } = view.state;
1029+
1030+
const imsOrgId = values.ims_org_input?.ims_org_id?.value?.trim();
1031+
const metadata = JSON.parse(view.private_metadata || '{}');
1032+
const { originalChannel, originalThreadTs } = metadata;
1033+
1034+
if (!imsOrgId) {
1035+
await ack({
1036+
response_action: 'errors',
1037+
errors: {
1038+
ims_org_input: 'IMS Organization ID is required',
1039+
},
1040+
});
1041+
return;
1042+
}
1043+
1044+
await ack();
1045+
const responseChannel = originalChannel || body.user.id;
1046+
const responseThreadTs = originalChannel ? originalThreadTs : undefined;
1047+
1048+
const slackContext = {
1049+
say: async (message) => {
1050+
await client.chat.postMessage({
1051+
channel: responseChannel,
1052+
text: message,
1053+
thread_ts: responseThreadTs,
1054+
});
1055+
},
1056+
client,
1057+
channelId: responseChannel,
1058+
threadTs: responseThreadTs,
1059+
};
1060+
1061+
await slackContext.say(`:gear: Starting LLMO IMS org onboarding for *${imsOrgId}*...`);
1062+
1063+
// Create or find organization
1064+
const { Organization } = dataAccess;
1065+
let organization = await Organization.findByImsOrgId(imsOrgId);
1066+
1067+
if (!organization) {
1068+
log.info(`Creating new organization for IMS Org ID: ${imsOrgId}`);
1069+
await slackContext.say(`Creating organization for IMS Org ID: ${imsOrgId}`);
1070+
1071+
// Fetch IMS org details
1072+
let imsOrgDetails;
1073+
try {
1074+
imsOrgDetails = await imsClient.getImsOrganizationDetails(imsOrgId);
1075+
} catch (error) {
1076+
log.error(`Error retrieving IMS Org details: ${error.message}`);
1077+
await slackContext.say(`:x: Could not find an IMS org with the ID *${imsOrgId}*.`);
1078+
return;
1079+
}
1080+
1081+
if (!imsOrgDetails) {
1082+
await slackContext.say(`:x: Could not find an IMS org with the ID *${imsOrgId}*.`);
1083+
return;
1084+
}
1085+
1086+
organization = await Organization.create({
1087+
name: imsOrgDetails.orgName,
1088+
imsOrgId,
1089+
});
1090+
await organization.save();
1091+
log.info(`Created organization ${organization.getId()} for IMS Org ID: ${imsOrgId}`);
1092+
} else {
1093+
log.info(`Found existing organization ${organization.getId()} for IMS Org ID: ${imsOrgId}`);
1094+
await slackContext.say(`Found existing organization for IMS Org ID: ${imsOrgId}`);
1095+
}
1096+
1097+
try {
1098+
const tierClient = TierClient.createForOrg(lambdaContext, organization, LLMO_PRODUCT_CODE);
1099+
const { entitlement: existingEntitlement } = await tierClient.checkValidEntitlement();
1100+
const { entitlement } = await tierClient.createEntitlement(LLMO_TIER);
1101+
const wasNewlyCreated = !existingEntitlement
1102+
|| existingEntitlement.getId() !== entitlement.getId();
1103+
1104+
if (wasNewlyCreated) {
1105+
log.info(`Successfully created LLMO entitlement ${entitlement.getId()} for organization ${organization.getId()}`);
1106+
} else {
1107+
log.info(`Found existing LLMO entitlement ${entitlement.getId()} for organization ${organization.getId()}`);
1108+
}
1109+
1110+
await slackContext.say(`:white_check_mark: *LLMO IMS org onboarding completed successfully!*
1111+
1112+
*Organization:* ${organization.getName()}
1113+
*IMS Org ID:* ${imsOrgId}
1114+
*Entitlement ID:* ${entitlement.getId()}
1115+
*Tier:* ${LLMO_TIER}
1116+
*Status:* ${wasNewlyCreated ? 'New entitlement created' : 'Existing entitlement found and verified'}
1117+
1118+
The organization has been onboarded for LLMO.`);
1119+
} catch (error) {
1120+
log.error(`Error creating entitlement for organization: ${error.message}`);
1121+
await slackContext.say(`:x: Failed to create LLMO entitlement for organization: ${error.message}`);
1122+
return;
1123+
}
1124+
1125+
log.debug(`IMS org onboarding completed for ${imsOrgId} by user ${user.id}`);
1126+
} catch (error) {
1127+
log.error('Error handling IMS org onboarding modal:', error);
1128+
await ack({
1129+
response_action: 'errors',
1130+
errors: {
1131+
ims_org_input: 'There was an error processing the onboarding request.',
1132+
},
1133+
});
1134+
}
1135+
};
1136+
}

src/support/slack/commands/llmo-onboard.js

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ function LlmoOnboardCommand(context) {
3030
const baseCommand = BaseCommand({
3131
id: 'onboard-llmo',
3232
name: 'Onboard LLMO',
33-
description: 'Onboards a site for LLMO (Large Language Model Optimizer) through a modal interface.',
33+
description: 'Onboards a site or IMS org for LLMO (Large Language Model Optimizer) through a modal interface.',
3434
phrases: PHRASES,
35-
usageText: `${PHRASES[0]} <site url>`,
35+
usageText: `${PHRASES[0]} [site url]`,
3636
});
3737

3838
const { log } = context;
@@ -51,6 +51,39 @@ function LlmoOnboardCommand(context) {
5151

5252
const [site] = args;
5353

54+
// If no site parameter provided, trigger IMS org onboarding flow
55+
if (!site) {
56+
const message = {
57+
blocks: [
58+
{
59+
type: 'section',
60+
text: {
61+
type: 'mrkdwn',
62+
text: ':rocket: *LLMO IMS Org Onboarding*\n\nClick the button below to start the IMS organization onboarding process.',
63+
},
64+
},
65+
{
66+
type: 'actions',
67+
elements: [
68+
{
69+
type: 'button',
70+
text: {
71+
type: 'plain_text',
72+
text: 'Start Onboarding',
73+
},
74+
value: 'org_onboarding',
75+
action_id: 'start_llmo_org_onboarding',
76+
style: 'primary',
77+
},
78+
],
79+
},
80+
],
81+
thread_ts: threadTs,
82+
};
83+
await say(message);
84+
return;
85+
}
86+
5487
const normalizedSite = extractURLFromSlackInput(site);
5588

5689
if (!normalizedSite) {

0 commit comments

Comments
 (0)