|
1 | 1 | import { FolderScopedService } from '../../folder-scoped'; |
2 | | -import { RawJobGetResponse, JobGetAllOptions } from '../../../models/orchestrator/jobs.types'; |
3 | | -import { RawJobOutputFields } from '../../../models/orchestrator/jobs.internal-types'; |
| 2 | +import { RawJobGetResponse, JobGetAllOptions, JobGetByIdOptions } from '../../../models/orchestrator/jobs.types'; |
4 | 3 | import { JobServiceModel, JobGetResponse, createJobWithMethods } from '../../../models/orchestrator/jobs.models'; |
5 | | -import { pascalToCamelCaseKeys, transformData } from '../../../utils/transform'; |
| 4 | +import { addPrefixToKeys, pascalToCamelCaseKeys, transformData } from '../../../utils/transform'; |
6 | 5 | import { JOB_ENDPOINTS } from '../../../utils/constants/endpoints'; |
7 | | -import { ODATA_PAGINATION, ODATA_OFFSET_PARAMS } from '../../../utils/constants/common'; |
| 6 | +import { ODATA_PAGINATION, ODATA_OFFSET_PARAMS, ODATA_PREFIX } from '../../../utils/constants/common'; |
8 | 7 | import { JobMap } from '../../../models/orchestrator/jobs.constants'; |
9 | 8 | import { AttachmentService } from '../attachments/attachments'; |
10 | 9 | import { ValidationError, ServerError } from '../../../core/errors'; |
@@ -105,6 +104,58 @@ export class JobService extends FolderScopedService implements JobServiceModel { |
105 | 104 | }, options) as any; |
106 | 105 | } |
107 | 106 |
|
| 107 | + /** |
| 108 | + * Gets a job by its unique key (GUID). |
| 109 | + * |
| 110 | + * Returns the full job details including state, timing, input/output arguments, and error information. |
| 111 | + * Use `expand` to include related entities like `robot`, or `machine`. |
| 112 | + * |
| 113 | + * @param id - The unique key (GUID) of the job to retrieve |
| 114 | + * @param folderId - The folder ID where the job resides |
| 115 | + * @param options - Optional query options for expanding or selecting fields |
| 116 | + * @returns Promise resolving to a {@link JobGetResponse} with full job details and bound methods |
| 117 | + * |
| 118 | + * @example |
| 119 | + * ```typescript |
| 120 | + * // Get a job by key |
| 121 | + * const job = await jobs.getById(<id>, <folderId>); |
| 122 | + * console.log(job.state, job.processName); |
| 123 | + * ``` |
| 124 | + * |
| 125 | + * @example |
| 126 | + * ```typescript |
| 127 | + * // With expanded related entities |
| 128 | + * const job = await jobs.getById(<id>, <folderId>, { |
| 129 | + * expand: 'robot,machine' |
| 130 | + * }); |
| 131 | + * console.log(job.robot?.name, job.machine?.name); |
| 132 | + * ``` |
| 133 | + */ |
| 134 | + @track('Jobs.GetById') |
| 135 | + async getById(id: string, folderId: number, options?: JobGetByIdOptions): Promise<JobGetResponse> { |
| 136 | + if (!id) { |
| 137 | + throw new ValidationError({ message: 'id is required for getById' }); |
| 138 | + } |
| 139 | + if (!folderId) { |
| 140 | + throw new ValidationError({ message: 'folderId is required for getById' }); |
| 141 | + } |
| 142 | + |
| 143 | + const headers = createHeaders({ [FOLDER_ID]: folderId }); |
| 144 | + const keysToPrefix = Object.keys(options ?? {}); |
| 145 | + const apiOptions = options ? addPrefixToKeys(options, ODATA_PREFIX, keysToPrefix) : {}; |
| 146 | + |
| 147 | + const response = await this.get<Record<string, unknown>>( |
| 148 | + JOB_ENDPOINTS.GET_BY_KEY(id), |
| 149 | + { |
| 150 | + params: apiOptions, |
| 151 | + headers, |
| 152 | + } |
| 153 | + ); |
| 154 | + |
| 155 | + const rawJob = transformData(pascalToCamelCaseKeys(response.data) as RawJobGetResponse, JobMap); |
| 156 | + return createJobWithMethods(rawJob, this); |
| 157 | + } |
| 158 | + |
108 | 159 | /** |
109 | 160 | * Gets the output of a completed job. |
110 | 161 | * |
@@ -143,44 +194,23 @@ export class JobService extends FolderScopedService implements JobServiceModel { |
143 | 194 | throw new ValidationError({ message: 'jobKey is required for getOutput' }); |
144 | 195 | } |
145 | 196 |
|
146 | | - const job = await this.fetchJobByKey(jobKey, folderId); |
| 197 | + const job = await this.getById(jobKey, folderId, { select: 'outputArguments,outputFile' }); |
147 | 198 |
|
148 | | - if (job.OutputArguments) { |
| 199 | + if (job.outputArguments) { |
149 | 200 | try { |
150 | | - return JSON.parse(job.OutputArguments) as Record<string, unknown>; |
| 201 | + return JSON.parse(job.outputArguments) as Record<string, unknown>; |
151 | 202 | } catch { |
152 | 203 | throw new ServerError({ message: 'Failed to parse job output arguments as JSON' }); |
153 | 204 | } |
154 | 205 | } |
155 | 206 |
|
156 | | - if (job.OutputFile) { |
157 | | - return this.downloadOutputFile(job.OutputFile); |
| 207 | + if (job.outputFile) { |
| 208 | + return this.downloadOutputFile(job.outputFile); |
158 | 209 | } |
159 | 210 |
|
160 | 211 | return null; |
161 | 212 | } |
162 | 213 |
|
163 | | - /** |
164 | | - * Fetches a job by its Key (GUID) using the GetByKey endpoint. |
165 | | - * Only selects fields needed for output extraction. |
166 | | - */ |
167 | | - private async fetchJobByKey( |
168 | | - jobKey: string, |
169 | | - folderId: number |
170 | | - ): Promise<RawJobOutputFields> { |
171 | | - const headers = createHeaders({ [FOLDER_ID]: folderId }); |
172 | | - const response = await this.get<RawJobOutputFields>( |
173 | | - JOB_ENDPOINTS.GET_BY_KEY(jobKey), |
174 | | - { |
175 | | - params: { |
176 | | - $select: 'OutputArguments,OutputFile', |
177 | | - }, |
178 | | - headers, |
179 | | - } |
180 | | - ); |
181 | | - return response.data; |
182 | | - } |
183 | | - |
184 | 214 | /** |
185 | 215 | * Downloads the output file content via the Attachments API. |
186 | 216 | * 1. Fetches blob access info from the attachment using AttachmentService |
|
0 commit comments