Skip to content

Commit 7d00158

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
fix(oracle-epm-account-reconciliation): address validation findings
1 parent 7ca8782 commit 7d00158

23 files changed

Lines changed: 229 additions & 91 deletions

apps/docs/content/docs/integrations/oracle_epm_account_reconciliation.mdx

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,6 @@ Read the status of one Reconciliation Compliance job.
218218
| `details` | string | Documented provider details; counts remain provider text |
219219
| `state` | string | Normalized job state: pending, succeeded, or failed |
220220
| `jobId` | string | Job ID extracted from a validated provider status link |
221-
| `accepted` | boolean | Whether Oracle accepted the launch; preserved if later work fails |
222221

223222
### Oracle EPM Account Reconciliation Get Matching Job Status
224223

@@ -239,7 +238,6 @@ Read one Transaction Matching job status and validated artifact filenames.
239238
| `details` | string | Documented provider details; counts remain provider text |
240239
| `state` | string | Normalized job state: pending, succeeded, or failed |
241240
| `jobId` | string | Job ID extracted from a validated provider status link |
242-
| `accepted` | boolean | Whether Oracle accepted the launch; preserved if later work fails |
243241
| `logFileName` | string | Repository log filename extracted from a validated log-content link |
244242
| `archiveFileName` | string | Repository archive filename extracted from a validated file-content link |
245243

@@ -304,7 +302,7 @@ Import pre-mapped transactions into a Transaction Matching data source.
304302
| `oauthCredential` | string | Yes | Oracle EPM service-account credential |
305303
| `fileName` | string | Yes | Exact staged transaction filename |
306304
| `matchTypeId` | string | Yes | Text ID of the Transaction Matching match type |
307-
| `dataSource` | string | Yes | Name of the Transaction Matching data source |
305+
| `dataSource` | string | Yes | Text ID of the Transaction Matching data source |
308306
| `dateFormat` | string | Yes | Date format used in the import file, for example MMM d, yyyy |
309307
| `waitForCompletion` | boolean | No | Wait for the accepted job to finish \(default false\) |
310308
| `maxWaitSeconds` | number | No | Maximum wait in seconds \(5–300; default 60\) |
@@ -319,7 +317,6 @@ Import pre-mapped transactions into a Transaction Matching data source.
319317
| `jobId` | string | Job ID extracted from a validated provider status link |
320318
| `accepted` | boolean | Whether Oracle accepted the launch; preserved if later work fails |
321319
| `logFileName` | string | Repository log filename extracted from a validated log-content link |
322-
| `archiveFileName` | string | Repository archive filename extracted from a validated file-content link |
323320

324321
### Oracle EPM Account Reconciliation Import Premapped Balances
325322

@@ -556,7 +553,7 @@ Check whether all reconciliations selected by a public filter are closed.
556553

557554
| Parameter | Type | Description |
558555
| --------- | ---- | ----------- |
559-
| `status` | number | Oracle operation status: -1 in progress, 0 success, positive failure |
556+
| `status` | number | Monitor status: -1 some reconciliations remain open, 0 all closed, positive failure |
560557
| `details` | string | Documented provider details; counts remain provider text |
561558
| `allClosed` | boolean | True only when all filtered reconciliations are closed; -1 means some remain open |
562559

@@ -584,7 +581,6 @@ Permanently purge transactions from a completed archive job.
584581
| `jobId` | string | Job ID extracted from a validated provider status link |
585582
| `accepted` | boolean | Whether Oracle accepted the launch; preserved if later work fails |
586583
| `logFileName` | string | Repository log filename extracted from a validated log-content link |
587-
| `archiveFileName` | string | Repository archive filename extracted from a validated file-content link |
588584

589585
### Oracle EPM Account Reconciliation Purge Matched Transactions
590586

@@ -613,7 +609,6 @@ Permanently purge matched transactions older than the specified age.
613609
| `jobId` | string | Job ID extracted from a validated provider status link |
614610
| `accepted` | boolean | Whether Oracle accepted the launch; preserved if later work fails |
615611
| `logFileName` | string | Repository log filename extracted from a validated log-content link |
616-
| `archiveFileName` | string | Repository archive filename extracted from a validated file-content link |
617612

618613
### Oracle EPM Account Reconciliation Remove Users from Team
619614

@@ -661,8 +656,6 @@ Run automatic alerts for a Transaction Matching match type.
661656
| `state` | string | Normalized job state: pending, succeeded, or failed |
662657
| `jobId` | string | Job ID extracted from a validated provider status link |
663658
| `accepted` | boolean | Whether Oracle accepted the launch; preserved if later work fails |
664-
| `logFileName` | string | Repository log filename extracted from a validated log-content link |
665-
| `archiveFileName` | string | Repository archive filename extracted from a validated file-content link |
666659

667660
### Oracle EPM Account Reconciliation Run Auto Match
668661

@@ -686,8 +679,6 @@ Run automatic matching for a Transaction Matching match type.
686679
| `state` | string | Normalized job state: pending, succeeded, or failed |
687680
| `jobId` | string | Job ID extracted from a validated provider status link |
688681
| `accepted` | boolean | Whether Oracle accepted the launch; preserved if later work fails |
689-
| `logFileName` | string | Repository log filename extracted from a validated log-content link |
690-
| `archiveFileName` | string | Repository archive filename extracted from a validated file-content link |
691682

692683
### Oracle EPM Account Reconciliation Run Profile Rules
693684

@@ -787,7 +778,6 @@ Unmatch all transactions matched by a specified auto-match job.
787778
| `jobId` | string | Job ID extracted from a validated provider status link |
788779
| `accepted` | boolean | Whether Oracle accepted the launch; preserved if later work fails |
789780
| `logFileName` | string | Repository log filename extracted from a validated log-content link |
790-
| `archiveFileName` | string | Repository archive filename extracted from a validated file-content link |
791781

792782
### Oracle EPM Account Reconciliation Unmatch Transactions
793783

@@ -814,7 +804,6 @@ Unmatch up to 10,000 specified matches in a match type.
814804
| `jobId` | string | Job ID extracted from a validated provider status link |
815805
| `accepted` | boolean | Whether Oracle accepted the launch; preserved if later work fails |
816806
| `logFileName` | string | Repository log filename extracted from a validated log-content link |
817-
| `archiveFileName` | string | Repository archive filename extracted from a validated file-content link |
818807

819808
### Oracle EPM Account Reconciliation Upload File
820809

apps/sim/blocks/blocks/oracle_epm_account_reconciliation.ts

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -821,7 +821,7 @@ export const OracleEpmAccountReconciliationBlock: BlockConfig<OracleEpmAccountRe
821821
'purge_archived_transactions',
822822
],
823823
},
824-
placeholder: 'Reconciliation Compliance job ID',
824+
placeholder: 'Job ID; for purge, use the completed archive job ID',
825825
},
826826
{
827827
id: 'dataLoadDefinition',
@@ -893,7 +893,7 @@ export const OracleEpmAccountReconciliationBlock: BlockConfig<OracleEpmAccountRe
893893
field: 'operation',
894894
value: ['import_matching_transactions'],
895895
},
896-
placeholder: 'Name of the Transaction Matching data source',
896+
placeholder: 'Text ID of the Transaction Matching data source',
897897
},
898898
{
899899
id: 'balanceType',
@@ -1327,7 +1327,7 @@ export const OracleEpmAccountReconciliationBlock: BlockConfig<OracleEpmAccountRe
13271327
'oracle_epm_account_reconciliation_import_premapped_balances',
13281328
'oracle_epm_account_reconciliation_import_profiles',
13291329
'oracle_epm_account_reconciliation_import_rates',
1330-
'oracle_epm_account_reconciliation_import_reconciliation_attributes',
1330+
'oracle_epm_account_reconciliation_import_recon_attributes',
13311331
'oracle_epm_account_reconciliation_list_files',
13321332
'oracle_epm_account_reconciliation_list_periods',
13331333
'oracle_epm_account_reconciliation_list_reconciliation_comments',
@@ -1346,7 +1346,14 @@ export const OracleEpmAccountReconciliationBlock: BlockConfig<OracleEpmAccountRe
13461346
'oracle_epm_account_reconciliation_upload_file',
13471347
],
13481348
config: {
1349-
tool: (params) => `oracle_epm_account_reconciliation_${params.operation ?? 'list_periods'}`,
1349+
tool: (params) => {
1350+
switch (params.operation) {
1351+
case 'import_reconciliation_attributes':
1352+
return 'oracle_epm_account_reconciliation_import_recon_attributes'
1353+
default:
1354+
return `oracle_epm_account_reconciliation_${params.operation ?? 'list_periods'}`
1355+
}
1356+
},
13501357
params: (params) => {
13511358
switch (params.operation ?? 'list_periods') {
13521359
case 'add_users_to_team':
@@ -1753,7 +1760,7 @@ export const OracleEpmAccountReconciliationBlock: BlockConfig<OracleEpmAccountRe
17531760
},
17541761
jobId: {
17551762
type: 'string',
1756-
description: 'Reconciliation Compliance job ID',
1763+
description: 'Compliance or Matching job ID; for purge, use the completed archive job ID',
17571764
},
17581765
dataLoadDefinition: {
17591766
type: 'string',
@@ -1769,7 +1776,7 @@ export const OracleEpmAccountReconciliationBlock: BlockConfig<OracleEpmAccountRe
17691776
},
17701777
dataSource: {
17711778
type: 'string',
1772-
description: 'Name of the Transaction Matching data source',
1779+
description: 'Text ID of the Transaction Matching data source',
17731780
},
17741781
balanceType: {
17751782
type: 'string',
@@ -1877,7 +1884,8 @@ export const OracleEpmAccountReconciliationBlock: BlockConfig<OracleEpmAccountRe
18771884
outputs: {
18781885
status: {
18791886
type: 'number',
1880-
description: 'Oracle operation status: -1 in progress, 0 success, positive failure',
1887+
description:
1888+
'Oracle status: -1 pending job (Monitor: some reconciliations remain open), 0 success (Monitor: all closed), positive failure',
18811889
condition: {
18821890
field: 'operation',
18831891
value: [
@@ -2030,8 +2038,6 @@ export const OracleEpmAccountReconciliationBlock: BlockConfig<OracleEpmAccountRe
20302038
'create_reconciliations',
20312039
'delete_profile',
20322040
'export_user_details_report',
2033-
'get_compliance_job_status',
2034-
'get_matching_job_status',
20352041
'import_balances',
20362042
'import_compliance_transactions',
20372043
'import_matching_transactions',
@@ -2063,8 +2069,6 @@ export const OracleEpmAccountReconciliationBlock: BlockConfig<OracleEpmAccountRe
20632069
'import_matching_transactions',
20642070
'purge_archived_transactions',
20652071
'purge_matched_transactions',
2066-
'run_auto_alert',
2067-
'run_auto_match',
20682072
'unmatch_auto_match_job',
20692073
'unmatch_transactions',
20702074
],
@@ -2075,17 +2079,7 @@ export const OracleEpmAccountReconciliationBlock: BlockConfig<OracleEpmAccountRe
20752079
description: 'Repository archive filename extracted from a validated file-content link',
20762080
condition: {
20772081
field: 'operation',
2078-
value: [
2079-
'archive_matched_transactions',
2080-
'get_matching_job_status',
2081-
'import_matching_transactions',
2082-
'purge_archived_transactions',
2083-
'purge_matched_transactions',
2084-
'run_auto_alert',
2085-
'run_auto_match',
2086-
'unmatch_auto_match_job',
2087-
'unmatch_transactions',
2088-
],
2082+
value: ['archive_matched_transactions', 'get_matching_job_status'],
20892083
},
20902084
},
20912085
fileName: {

apps/sim/lib/internal/oracle-epm-account-reconciliation/execute-tool.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,7 @@ const cases = [
564564

565565
async function invoke(id: string, input: object) {
566566
const response = await executeOracleEpmAccountReconciliationTool({
567-
toolId: `oracle_epm_account_reconciliation_${id}`,
567+
toolId: `oracle_epm_account_reconciliation_${id === 'import_reconciliation_attributes' ? 'import_recon_attributes' : id}`,
568568
input: { ...AUTH, ...input },
569569
context,
570570
headers: new Headers(),

apps/sim/lib/internal/oracle-epm-account-reconciliation/execute-tool.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ export const executeOracleEpmAccountReconciliationTool: InternalToolOperationHan
120120
executeOracleEpmAccountReconciliationImportRatesOperation,
121121
request
122122
)
123-
case 'oracle_epm_account_reconciliation_import_reconciliation_attributes':
123+
case 'oracle_epm_account_reconciliation_import_recon_attributes':
124124
return executeToolOperationImplementation(
125125
executeOracleEpmAccountReconciliationImportReconciliationAttributesOperation,
126126
request

apps/sim/lib/internal/oracle-epm-account-reconciliation/jobs.test.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,90 @@ describe('Account Reconciliation job behavior', () => {
173173
expect(mocks.fetch).toHaveBeenCalledTimes(1)
174174
}
175175
)
176+
it('preserves an already-open period when the initial opening job has failed', async () => {
177+
mocks.fetch.mockResolvedValueOnce(
178+
response(1, {
179+
links: [{ rel: 'Job Status', action: 'GET', href: `${origin}/armARCS/rest/v1/jobs/42` }],
180+
})
181+
)
182+
const result = await launchArcsJob(
183+
client,
184+
'compliance',
185+
'SET_PERIOD_STATUS',
186+
{ period: 'Jan', status: 'open' },
187+
{ periodStatus: 'open', waitForCompletion: true }
188+
)
189+
expect(result).toMatchObject({
190+
success: false,
191+
output: { status: 1, state: 'failed', accepted: true, periodStatus: 'open', jobId: '42' },
192+
})
193+
expect(mocks.fetch).toHaveBeenCalledTimes(1)
194+
})
195+
it.each([
196+
{ links: [] },
197+
{
198+
links: [
199+
{
200+
rel: 'Job Status',
201+
action: 'GET',
202+
href: 'https://attacker.example/armARCS/rest/v1/jobs/42',
203+
},
204+
],
205+
},
206+
{ links: [{ rel: 'Job Status', action: 'POST', href: `${origin}/armARCS/rest/v1/jobs/42` }] },
207+
])(
208+
'does not claim an applied period change without a valid failed opening-job link ($links)',
209+
async ({ links }) => {
210+
mocks.fetch.mockResolvedValueOnce(response(1, { links }))
211+
const result = await launchArcsJob(
212+
client,
213+
'compliance',
214+
'SET_PERIOD_STATUS',
215+
{ period: 'Jan', status: 'open' },
216+
{ periodStatus: 'open' }
217+
)
218+
expect(result).toMatchObject({ success: false, output: { status: 1, state: 'failed' } })
219+
expect(result.output).not.toHaveProperty('accepted')
220+
expect(result.output).not.toHaveProperty('periodStatus')
221+
expect(result.output).not.toHaveProperty('jobId')
222+
expect(mocks.fetch).toHaveBeenCalledTimes(1)
223+
}
224+
)
225+
it.each([
226+
['runautomatch', false, false],
227+
['runautoalert', false, false],
228+
['archivetransactions', true, true],
229+
['purgetransactions', true, false],
230+
['purgearchivetransactions', true, false],
231+
['importtmpremappedtransactions', true, false],
232+
['unmatchtransactions', true, false],
233+
['unmatchtransactionsbyautomatch', true, false],
234+
] as const)(
235+
'projects only documented artifacts for %s on launch and after waiting',
236+
async (jobName, hasLog, hasArchive) => {
237+
const links = [
238+
{ rel: 'Job Status', action: 'GET', href },
239+
{
240+
rel: 'log-content',
241+
action: 'GET',
242+
href: `${origin}/rest/applicationsnapshots/log.txt/contents`,
243+
},
244+
{
245+
rel: 'file-content',
246+
action: 'GET',
247+
href: `${origin}/rest/applicationsnapshots/archive.zip/contents`,
248+
},
249+
]
250+
for (const waitForCompletion of [false, true]) {
251+
mocks.fetch.mockResolvedValueOnce(response(-1, { links }))
252+
if (waitForCompletion) mocks.fetch.mockResolvedValueOnce(response(0, { links }))
253+
const result = await launchArcsJob(client, 'matching', jobName, {}, { waitForCompletion })
254+
expect(result.success).toBe(true)
255+
expect(result.output.logFileName).toBe(hasLog ? 'log.txt' : undefined)
256+
expect(result.output.archiveFileName).toBe(hasArchive ? 'archive.zip' : undefined)
257+
}
258+
}
259+
)
176260
it('projects validated matching artifacts without parsing counts from details', async () => {
177261
mocks.fetch.mockResolvedValueOnce(
178262
response(0, {

apps/sim/lib/internal/oracle-epm-account-reconciliation/jobs.ts

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,16 @@ import type {
2323

2424
export type ArcsJobKind = keyof typeof arcsJobLinkPolicies
2525

26+
type ArcsArtifactRelation = 'log-content' | 'file-content'
27+
const matchingJobArtifacts: Record<string, readonly ArcsArtifactRelation[]> = {
28+
archivetransactions: ['log-content', 'file-content'],
29+
purgetransactions: ['log-content'],
30+
purgearchivetransactions: ['log-content'],
31+
importtmpremappedtransactions: ['log-content'],
32+
unmatchtransactions: ['log-content'],
33+
unmatchtransactionsbyautomatch: ['log-content'],
34+
}
35+
2636
export function classifyArcsStatus(status: number): 'pending' | 'succeeded' | 'failed' {
2737
return status === -1 ? 'pending' : status === 0 ? 'succeeded' : 'failed'
2838
}
@@ -70,7 +80,8 @@ export function projectArcsJob(
7080
client: OracleEpmClient,
7181
kind: ArcsJobKind,
7282
job: ArcsJob,
73-
jobId?: string
83+
jobId?: string,
84+
allowedArtifacts: readonly ArcsArtifactRelation[] = ['log-content', 'file-content']
7485
): OracleEpmAccountReconciliationJobOutput {
7586
const output: OracleEpmAccountReconciliationJobOutput = {
7687
status: job.status,
@@ -80,9 +91,9 @@ export function projectArcsJob(
8091
}
8192
if (kind === 'matching') {
8293
for (const link of job.links ?? []) {
83-
if (link.rel === 'log-content')
94+
if (link.rel === 'log-content' && allowedArtifacts.includes('log-content'))
8495
output.logFileName = resolveArcsArtifact(client, link).fileName
85-
if (link.rel === 'file-content')
96+
if (link.rel === 'file-content' && allowedArtifacts.includes('file-content'))
8697
output.archiveFileName = resolveArcsArtifact(client, link).fileName
8798
}
8899
}
@@ -155,7 +166,20 @@ export async function launchArcsJob(
155166
details: job.details ?? null,
156167
state: classifyArcsStatus(job.status),
157168
}
158-
if (job.status > 0) return { success: false, error: 'Oracle EPM rejected the job', output }
169+
if (job.status > 0) {
170+
// Opening changes the period immediately; a returned job tracks reconciliation opening.
171+
if (options.periodStatus === 'open') {
172+
const resolved = resolveArcsJobLink(client, kind, job)
173+
if (resolved) {
174+
return {
175+
success: false,
176+
error: 'Oracle EPM reconciliation-opening job completed with errors',
177+
output: { ...output, accepted: true, periodStatus: 'open', jobId: resolved.jobId },
178+
}
179+
}
180+
}
181+
return { success: false, error: 'Oracle EPM rejected the job', output }
182+
}
159183
output.accepted = true
160184
if (options.periodStatus) output.periodStatus = options.periodStatus
161185
if (options.periodStatus && options.periodStatus !== 'open') {
@@ -167,7 +191,8 @@ export async function launchArcsJob(
167191
throw new ArcsContractError(
168192
'Oracle EPM accepted the job but did not return a valid status link'
169193
)
170-
output = { ...output, ...projectArcsJob(client, kind, job, resolved?.jobId) }
194+
const allowedArtifacts = matchingJobArtifacts[jobName] ?? []
195+
output = { ...output, ...projectArcsJob(client, kind, job, resolved?.jobId, allowedArtifacts) }
171196
if (!options.waitForCompletion || job.status !== -1 || !resolved)
172197
return { success: true, output }
173198
const completed = await waitForArcsJob(
@@ -176,7 +201,10 @@ export async function launchArcsJob(
176201
options.maxWaitSeconds ?? 60,
177202
signal
178203
)
179-
output = { ...output, ...projectArcsJob(client, kind, completed, resolved.jobId) }
204+
output = {
205+
...output,
206+
...projectArcsJob(client, kind, completed, resolved.jobId, allowedArtifacts),
207+
}
180208
return completed.status === 0
181209
? { success: true, output }
182210
: { success: false, error: 'Oracle EPM job completed with errors', output }

0 commit comments

Comments
 (0)