Skip to content

Commit 0823270

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
feat(integrations): add Oracle Fusion Subscription Management
1 parent 15294a2 commit 0823270

95 files changed

Lines changed: 16605 additions & 8 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/docs/components/ui/icon-mapping.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
480480
onedrive: MicrosoftOneDriveIcon,
481481
onepassword: OnePasswordIcon,
482482
openai: OpenAIIcon,
483+
oracle_fusion_subscription_management: NetSuiteIcon,
483484
outlook: OutlookIcon,
484485
pagerduty: PagerDutyIcon,
485486
parallel_ai: ParallelIcon,

apps/docs/content/docs/integrations/meta.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@
187187
"onedrive",
188188
"onepassword",
189189
"openai",
190+
"oracle_fusion_subscription_management",
190191
"outlook",
191192
"pagerduty",
192193
"parallel_ai",

apps/docs/content/docs/integrations/oracle_fusion_subscription_management.mdx

Lines changed: 2505 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/**
2+
* @vitest-environment node
3+
*/
4+
import { describe, expect, it, vi } from 'vitest'
5+
6+
vi.mock('@/components/icons', () => ({ NetSuiteIcon: () => null }))
7+
vi.mock('@/lib/oauth/utils', () => ({ getScopesForService: () => [] }))
8+
9+
import { parseOracleFusionSubscriptionInput } from '@/lib/internal/oracle-fusion-subscription-management/schema'
10+
import { buildSelectorContextFromValues } from '@/lib/selectors/context'
11+
import {
12+
buildCanonicalIndex,
13+
isSubBlockVisibleForMode,
14+
resolveActiveCanonicalValue,
15+
} from '@/lib/workflows/subblocks/visibility'
16+
import { OracleFusionSubscriptionManagementBlock } from '@/blocks/blocks/oracle_fusion_subscription_management'
17+
18+
const block = OracleFusionSubscriptionManagementBlock
19+
function mapped(name: string, values: Record<string, unknown>) {
20+
return {
21+
...block.tools.config.params?.({
22+
operation: `oracle_fusion_subscription_management_${name}`,
23+
oauthCredential: 'credential-1',
24+
...values,
25+
}),
26+
oauthCredential: 'credential-1',
27+
accessToken: 'test-token',
28+
instanceUrl: 'https://vision.fa.us2.oraclecloud.com',
29+
}
30+
}
31+
32+
describe('Subscription Management canonical block mapping', () => {
33+
it('keeps existing-key pickers visible through the unfiltered canonical index', () => {
34+
const index = buildCanonicalIndex(block.subBlocks)
35+
for (const canonical of ['subscriptionNumber', 'subscriptionProductPuid', 'coveredLevelPuid']) {
36+
const selector = block.subBlocks.find((field) => field.id === `${canonical}Selector`)!
37+
expect(index.groupsById[canonical].basicId).toBe(selector.id)
38+
expect(index.groupsById[canonical].advancedIds).toEqual([`${canonical}Manual`])
39+
expect(isSubBlockVisibleForMode(selector, false, index, {}, { [canonical]: 'basic' })).toBe(
40+
true
41+
)
42+
}
43+
const input = mapped('create_covered_level', {
44+
subscriptionNumber: 'SUB-001',
45+
subscriptionProductPuid: 'P-001',
46+
newCoveredLevelPuid: 'NEW-C-001',
47+
coveredLevelPuid: 'STALE-C-001',
48+
type: 'TENANT_COVERAGE_CODE',
49+
startDate: '2026-09-01',
50+
})
51+
expect(parseOracleFusionSubscriptionInput('create_covered_level', input)).toMatchObject({
52+
newCoveredLevelPuid: 'NEW-C-001',
53+
})
54+
expect(input).not.toHaveProperty('coveredLevelPuid')
55+
})
56+
57+
it.each([
58+
'update_product',
59+
'create_covered_level',
60+
'update_covered_level',
61+
'create_associated_asset',
62+
'update_associated_asset',
63+
])('keeps organization available only as item-picker context for %s', (name) => {
64+
const values = {
65+
operation: `oracle_fusion_subscription_management_${name}`,
66+
credential: 'credential-1',
67+
definitionOrganizationIdSelector: '123',
68+
}
69+
expect(
70+
buildSelectorContextFromValues({
71+
selectorKey: 'oracleFusionSubscriptionManagement.subscriptionItems',
72+
contextConfigs: block.subBlocks,
73+
values,
74+
dependsOn: ['oauthCredential', 'definitionOrganizationId'],
75+
})
76+
).toEqual({ oauthCredential: 'credential-1', orgId: '123' })
77+
expect(mapped(name, { definitionOrganizationId: '123' })).not.toHaveProperty(
78+
'definitionOrganizationId'
79+
)
80+
})
81+
82+
it('preserves exact resolved identifiers and drops stale lifecycle fields after operation changes', () => {
83+
const input = mapped('update_product', {
84+
subscriptionNumber: 'SUB / 001',
85+
subscriptionProductPuid: 'P-001',
86+
inventoryItemId: '9007199254740993',
87+
quantity: '0',
88+
suspendedDate: '2026-09-01',
89+
autoExtendFlag: 'true',
90+
})
91+
expect(parseOracleFusionSubscriptionInput('update_product', input)).toMatchObject({
92+
subscriptionNumber: 'SUB / 001',
93+
inventoryItemId: '9007199254740993',
94+
quantity: 0,
95+
})
96+
expect(input).not.toHaveProperty('suspendedDate')
97+
expect(input).not.toHaveProperty('autoExtendFlag')
98+
})
99+
100+
it('keeps the selected/manual public identifier canonical and out of numeric coercion', () => {
101+
const index = buildCanonicalIndex(block.subBlocks)
102+
const members = block.subBlocks.filter(
103+
(field) => field.canonicalParamId === 'subscriptionNumber'
104+
)
105+
expect(members.map((field) => field.id)).toEqual([
106+
'subscriptionNumberSelector',
107+
'subscriptionNumberManual',
108+
])
109+
expect(
110+
resolveActiveCanonicalValue(
111+
index.groupsById.subscriptionNumber,
112+
{
113+
subscriptionNumberSelector: 'SUB-OLD',
114+
subscriptionNumberManual: '<upstream.subscriptionNumber>',
115+
operation: 'oracle_fusion_subscription_management_get_subscription',
116+
},
117+
{ subscriptionNumber: 'advanced' }
118+
)
119+
).toBe('<upstream.subscriptionNumber>')
120+
})
121+
122+
it('maps billing scope independently and removes inactive covered-level context', () => {
123+
const values = {
124+
subscriptionNumber: 'SUB-001',
125+
subscriptionProductPuid: 'P-001',
126+
coveredLevelPuid: 'C-001',
127+
limit: '25',
128+
totalResults: 'false',
129+
}
130+
const product = mapped('list_bill_lines', { ...values, billingScope: 'product' })
131+
expect(product).not.toHaveProperty('coveredLevelPuid')
132+
expect(parseOracleFusionSubscriptionInput('list_bill_lines', product)).toMatchObject({
133+
billingScope: 'product',
134+
limit: 25,
135+
totalResults: false,
136+
})
137+
const covered = mapped('list_bill_lines', { ...values, billingScope: 'covered_level' })
138+
expect(parseOracleFusionSubscriptionInput('list_bill_lines', covered)).toMatchObject({
139+
billingScope: 'covered_level',
140+
coveredLevelPuid: 'C-001',
141+
})
142+
})
143+
144+
it('projects item selector organization dependencies from the active canonical field', () => {
145+
expect(
146+
buildSelectorContextFromValues({
147+
selectorKey: 'oracleFusionSubscriptionManagement.subscriptionItems',
148+
contextConfigs: block.subBlocks,
149+
values: {
150+
operation: 'oracle_fusion_subscription_management_create_product',
151+
credential: 'credential-1',
152+
definitionOrganizationIdSelector: '123',
153+
definitionOrganizationIdManual: '9007199254740993',
154+
instanceUrl: 'https://attacker.example',
155+
},
156+
canonicalModes: { definitionOrganizationId: 'advanced' },
157+
dependsOn: ['oauthCredential', 'definitionOrganizationId'],
158+
})
159+
).toEqual({ oauthCredential: 'credential-1', orgId: '9007199254740993' })
160+
})
161+
162+
it('distinguishes explicit false and invalid numbers from unset controls', () => {
163+
const values = { subscriptionNumber: 'SUB-001', subscriptionProductPuid: 'P-001' }
164+
expect(
165+
parseOracleFusionSubscriptionInput(
166+
'suspend_product',
167+
mapped('suspend_product', { ...values, autoExtendFlag: 'false', resumeDuration: '0' })
168+
)
169+
).toMatchObject({ autoExtendFlag: false, resumeDuration: 0 })
170+
expect(() =>
171+
parseOracleFusionSubscriptionInput(
172+
'suspend_product',
173+
mapped('suspend_product', { ...values, resumeDuration: 'invalid' })
174+
)
175+
).toThrow()
176+
})
177+
})

0 commit comments

Comments
 (0)