smartling-api-sdk-nodejs 3.3.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/.env.example +1 -1
  2. package/api/translation-memory/dto/async-action-result-dto.ts +12 -0
  3. package/api/translation-memory/dto/async-action-status.ts +8 -0
  4. package/api/translation-memory/dto/create-string-search-result-dto.ts +16 -0
  5. package/api/translation-memory/dto/duplicate-string-dto.ts +9 -0
  6. package/api/translation-memory/dto/locale-summary-dto.ts +10 -0
  7. package/api/translation-memory/dto/strings-export-async-action-result-dto.ts +8 -0
  8. package/api/translation-memory/dto/tm-dto.ts +23 -0
  9. package/api/translation-memory/dto/tm-locale-dto.ts +8 -0
  10. package/api/translation-memory/dto/tmx-import-summary-dto.ts +16 -0
  11. package/api/translation-memory/dto/tmx-parsing-error-dto.ts +7 -0
  12. package/api/translation-memory/dto/user-dto.ts +6 -0
  13. package/api/translation-memory/index.ts +368 -0
  14. package/api/translation-memory/params/bulk-export-tm-strings-parameters.ts +30 -0
  15. package/api/translation-memory/params/create-string-search-filter-parameters.ts +232 -0
  16. package/api/translation-memory/params/create-string-search-options.ts +8 -0
  17. package/api/translation-memory/params/create-tm-parameters.ts +28 -0
  18. package/api/translation-memory/params/escape-smp-unicode-parser-directive.ts +6 -0
  19. package/api/translation-memory/params/extended-export-metadata-type.ts +4 -0
  20. package/api/translation-memory/params/import-tmx-file-parameters.ts +76 -0
  21. package/api/translation-memory/params/keyword-search-type.ts +5 -0
  22. package/api/translation-memory/params/leverage-type.ts +5 -0
  23. package/api/translation-memory/params/search-keyword.ts +10 -0
  24. package/api/translation-memory/params/source-type.ts +7 -0
  25. package/api/translation-memory/params/string-search-tags.ts +7 -0
  26. package/api/translation-memory/params/translation-quality-type.ts +5 -0
  27. package/api/translation-memory/params/tu-translation-quality-type.ts +6 -0
  28. package/api/translation-memory/params/update-tm-parameters.ts +23 -0
  29. package/built/api/translation-memory/dto/async-action-result-dto.d.ts +10 -0
  30. package/built/api/translation-memory/dto/async-action-result-dto.js +3 -0
  31. package/built/api/translation-memory/dto/async-action-result-dto.js.map +1 -0
  32. package/built/api/translation-memory/dto/async-action-status.d.ts +8 -0
  33. package/built/api/translation-memory/dto/async-action-status.js +13 -0
  34. package/built/api/translation-memory/dto/async-action-status.js.map +1 -0
  35. package/built/api/translation-memory/dto/create-string-search-result-dto.d.ts +11 -0
  36. package/built/api/translation-memory/dto/create-string-search-result-dto.js +3 -0
  37. package/built/api/translation-memory/dto/create-string-search-result-dto.js.map +1 -0
  38. package/built/api/translation-memory/dto/duplicate-string-dto.d.ts +8 -0
  39. package/built/api/translation-memory/dto/duplicate-string-dto.js +3 -0
  40. package/built/api/translation-memory/dto/duplicate-string-dto.js.map +1 -0
  41. package/built/api/translation-memory/dto/locale-summary-dto.d.ts +8 -0
  42. package/built/api/translation-memory/dto/locale-summary-dto.js +3 -0
  43. package/built/api/translation-memory/dto/locale-summary-dto.js.map +1 -0
  44. package/built/api/translation-memory/dto/strings-export-async-action-result-dto.d.ts +6 -0
  45. package/built/api/translation-memory/dto/strings-export-async-action-result-dto.js +3 -0
  46. package/built/api/translation-memory/dto/strings-export-async-action-result-dto.js.map +1 -0
  47. package/built/api/translation-memory/dto/tm-dto.d.ts +19 -0
  48. package/built/api/translation-memory/dto/tm-dto.js +3 -0
  49. package/built/api/translation-memory/dto/tm-dto.js.map +1 -0
  50. package/built/api/translation-memory/dto/tm-locale-dto.d.ts +7 -0
  51. package/built/api/translation-memory/dto/tm-locale-dto.js +3 -0
  52. package/built/api/translation-memory/dto/tm-locale-dto.js.map +1 -0
  53. package/built/api/translation-memory/dto/tmx-import-summary-dto.d.ts +11 -0
  54. package/built/api/translation-memory/dto/tmx-import-summary-dto.js +3 -0
  55. package/built/api/translation-memory/dto/tmx-import-summary-dto.js.map +1 -0
  56. package/built/api/translation-memory/dto/tmx-parsing-error-dto.d.ts +6 -0
  57. package/built/api/translation-memory/dto/tmx-parsing-error-dto.js +3 -0
  58. package/built/api/translation-memory/dto/tmx-parsing-error-dto.js.map +1 -0
  59. package/built/api/translation-memory/dto/user-dto.d.ts +5 -0
  60. package/built/api/translation-memory/dto/user-dto.js +3 -0
  61. package/built/api/translation-memory/dto/user-dto.js.map +1 -0
  62. package/built/api/translation-memory/index.d.ts +27 -0
  63. package/built/api/translation-memory/index.js +250 -0
  64. package/built/api/translation-memory/index.js.map +1 -0
  65. package/built/api/translation-memory/params/bulk-export-tm-strings-parameters.d.ts +8 -0
  66. package/built/api/translation-memory/params/bulk-export-tm-strings-parameters.js +24 -0
  67. package/built/api/translation-memory/params/bulk-export-tm-strings-parameters.js.map +1 -0
  68. package/built/api/translation-memory/params/create-string-search-filter-parameters.d.ts +43 -0
  69. package/built/api/translation-memory/params/create-string-search-filter-parameters.js +151 -0
  70. package/built/api/translation-memory/params/create-string-search-filter-parameters.js.map +1 -0
  71. package/built/api/translation-memory/params/create-string-search-options.d.ts +7 -0
  72. package/built/api/translation-memory/params/create-string-search-options.js +3 -0
  73. package/built/api/translation-memory/params/create-string-search-options.js.map +1 -0
  74. package/built/api/translation-memory/params/create-tm-parameters.d.ts +8 -0
  75. package/built/api/translation-memory/params/create-tm-parameters.js +24 -0
  76. package/built/api/translation-memory/params/create-tm-parameters.js.map +1 -0
  77. package/built/api/translation-memory/params/escape-smp-unicode-parser-directive.d.ts +4 -0
  78. package/built/api/translation-memory/params/escape-smp-unicode-parser-directive.js +9 -0
  79. package/built/api/translation-memory/params/escape-smp-unicode-parser-directive.js.map +1 -0
  80. package/built/api/translation-memory/params/extended-export-metadata-type.d.ts +4 -0
  81. package/built/api/translation-memory/params/extended-export-metadata-type.js +9 -0
  82. package/built/api/translation-memory/params/extended-export-metadata-type.js.map +1 -0
  83. package/built/api/translation-memory/params/import-tmx-file-parameters.d.ts +17 -0
  84. package/built/api/translation-memory/params/import-tmx-file-parameters.js +80 -0
  85. package/built/api/translation-memory/params/import-tmx-file-parameters.js.map +1 -0
  86. package/built/api/translation-memory/params/keyword-search-type.d.ts +5 -0
  87. package/built/api/translation-memory/params/keyword-search-type.js +10 -0
  88. package/built/api/translation-memory/params/keyword-search-type.js.map +1 -0
  89. package/built/api/translation-memory/params/leverage-type.d.ts +5 -0
  90. package/built/api/translation-memory/params/leverage-type.js +10 -0
  91. package/built/api/translation-memory/params/leverage-type.js.map +1 -0
  92. package/built/api/translation-memory/params/search-keyword.d.ts +8 -0
  93. package/built/api/translation-memory/params/search-keyword.js +3 -0
  94. package/built/api/translation-memory/params/search-keyword.js.map +1 -0
  95. package/built/api/translation-memory/params/source-type.d.ts +7 -0
  96. package/built/api/translation-memory/params/source-type.js +12 -0
  97. package/built/api/translation-memory/params/source-type.js.map +1 -0
  98. package/built/api/translation-memory/params/string-search-tags.d.ts +6 -0
  99. package/built/api/translation-memory/params/string-search-tags.js +3 -0
  100. package/built/api/translation-memory/params/string-search-tags.js.map +1 -0
  101. package/built/api/translation-memory/params/translation-quality-type.d.ts +5 -0
  102. package/built/api/translation-memory/params/translation-quality-type.js +10 -0
  103. package/built/api/translation-memory/params/translation-quality-type.js.map +1 -0
  104. package/built/api/translation-memory/params/tu-translation-quality-type.d.ts +6 -0
  105. package/built/api/translation-memory/params/tu-translation-quality-type.js +11 -0
  106. package/built/api/translation-memory/params/tu-translation-quality-type.js.map +1 -0
  107. package/built/api/translation-memory/params/update-tm-parameters.d.ts +6 -0
  108. package/built/api/translation-memory/params/update-tm-parameters.js +20 -0
  109. package/built/api/translation-memory/params/update-tm-parameters.js.map +1 -0
  110. package/built/index.d.ts +27 -0
  111. package/built/index.js +27 -0
  112. package/built/index.js.map +1 -1
  113. package/built/package.json +1 -1
  114. package/built/test/integration/translation-memory.spec.d.ts +1 -0
  115. package/built/test/integration/translation-memory.spec.js +138 -0
  116. package/built/test/integration/translation-memory.spec.js.map +1 -0
  117. package/built/test/translation-memory.spec.d.ts +1 -0
  118. package/built/test/translation-memory.spec.js +712 -0
  119. package/built/test/translation-memory.spec.js.map +1 -0
  120. package/index.ts +27 -0
  121. package/package.json +1 -1
  122. package/test/data/sample.tmx +12 -0
  123. package/test/integration/translation-memory.spec.ts +182 -0
  124. package/test/translation-memory.spec.ts +868 -0
package/.env.example CHANGED
@@ -1,4 +1,4 @@
1
- # Credentials for test/integration/glossaries.spec.ts.
1
+ # Credentials for test/integration/glossaries.spec.ts and test/integration/translation-memory.spec.ts.
2
2
  # Copy this file to .env and fill in real values to run: npm run test:integration
3
3
  # Without a real .env, the integration suite skips (shows as pending) rather than failing.
4
4
 
@@ -0,0 +1,12 @@
1
+ import { AsyncActionStatus } from "./async-action-status";
2
+
3
+ interface AsyncActionResultDto {
4
+ actionUid: string;
5
+ totalCount: number;
6
+ processedCount: number;
7
+ errorCount: number;
8
+ executionComplete: boolean;
9
+ actionStatus: AsyncActionStatus;
10
+ }
11
+
12
+ export { AsyncActionResultDto };
@@ -0,0 +1,8 @@
1
+ export enum AsyncActionStatus {
2
+ QUEUEING = "QUEUEING",
3
+ EXECUTING = "EXECUTING",
4
+ SUCCESS = "SUCCESS",
5
+ FAILURE = "FAILURE",
6
+ CANCELED = "CANCELED",
7
+ STOPPED = "STOPPED"
8
+ }
@@ -0,0 +1,16 @@
1
+ import { DuplicateStringDto } from "./duplicate-string-dto";
2
+
3
+ interface CreateStringSearchResultDto {
4
+ searchUid: string;
5
+ stringCount: number | null;
6
+ wordCount: number | null;
7
+ duplicateStrings: Array<DuplicateStringDto> | null;
8
+ duplicateSearch: boolean | null;
9
+ // UID of the async action tracking search indexing, only present if indexing continues in
10
+ // the background - distinct from AsyncActionResultDto.actionUid (a bulk operation's own
11
+ // tracking UID, always present once that operation has started).
12
+ actionUid: string | null;
13
+ offlineReportUid: string | null;
14
+ }
15
+
16
+ export { CreateStringSearchResultDto };
@@ -0,0 +1,9 @@
1
+ interface DuplicateStringDto {
2
+ duplicateKey: string;
3
+ count: number;
4
+ srcText: string;
5
+ srcLocale: string;
6
+ translationLocale: string;
7
+ }
8
+
9
+ export { DuplicateStringDto };
@@ -0,0 +1,10 @@
1
+ import { TmLocaleDto } from "./tm-locale-dto";
2
+
3
+ interface LocaleSummaryDto {
4
+ translationLocaleId: string;
5
+ translationLocale: TmLocaleDto;
6
+ stringCount: number;
7
+ wordCount: number;
8
+ }
9
+
10
+ export { LocaleSummaryDto };
@@ -0,0 +1,8 @@
1
+ import { AsyncActionResultDto } from "./async-action-result-dto";
2
+
3
+ interface StringsExportAsyncActionResultDto extends AsyncActionResultDto {
4
+ tmxDownloadKey: string | null;
5
+ expired: boolean;
6
+ }
7
+
8
+ export { StringsExportAsyncActionResultDto };
@@ -0,0 +1,23 @@
1
+ import { UserDto } from "./user-dto";
2
+ import { TmLocaleDto } from "./tm-locale-dto";
3
+ import { LocaleSummaryDto } from "./locale-summary-dto";
4
+ import { TranslationQualityType } from "../params/translation-quality-type";
5
+
6
+ interface TmDto {
7
+ // Nullable per the real schema (TM.tmUid: ID, no "!"), but in practice always populated for
8
+ // a successful createTm/updateTm/deleteTm/searchTms result - it's the TM's own identifier.
9
+ tmUid: string | null;
10
+ name: string;
11
+ description: string | null;
12
+ sourceLocaleId: string;
13
+ sourceLocale: TmLocaleDto;
14
+ creator: UserDto;
15
+ created: Date;
16
+ lastModifier: UserDto;
17
+ lastModified: Date;
18
+ localeSummary: Array<LocaleSummaryDto> | null;
19
+ translationQuality: TranslationQualityType;
20
+ managedWordsEnabled: boolean;
21
+ }
22
+
23
+ export { TmDto };
@@ -0,0 +1,8 @@
1
+ interface TmLocaleDto {
2
+ localeId: string;
3
+ localeDescription: string;
4
+ rtlLanguage: boolean | null;
5
+ wordSpacing: boolean | null;
6
+ }
7
+
8
+ export { TmLocaleDto };
@@ -0,0 +1,16 @@
1
+ import { TmxParsingErrorDto } from "./tmx-parsing-error-dto";
2
+
3
+ interface TmxImportSummaryDto {
4
+ message: string | null;
5
+ wordCount: number;
6
+ stringCount: number;
7
+ // Errors rejecting specific translation units from the TM itself (e.g. a business-rule
8
+ // violation on an otherwise well-formed unit).
9
+ translationMemoryImportErrors: Array<TmxParsingErrorDto> | null;
10
+ // Errors in the TMX file's own XML/structure, encountered while parsing it.
11
+ parsingErrors: Array<TmxParsingErrorDto> | null;
12
+ jobUid: string;
13
+ tmxUid: string;
14
+ }
15
+
16
+ export { TmxImportSummaryDto };
@@ -0,0 +1,7 @@
1
+ interface TmxParsingErrorDto {
2
+ messages: Array<string>;
3
+ line: number;
4
+ column: number;
5
+ }
6
+
7
+ export { TmxParsingErrorDto };
@@ -0,0 +1,6 @@
1
+ interface UserDto {
2
+ userUid: string;
3
+ name: string;
4
+ }
5
+
6
+ export { UserDto };
@@ -0,0 +1,368 @@
1
+ import FormData from "form-data";
2
+ import { SmartlingBaseFileApi } from "../base";
3
+ import { AccessTokenProvider } from "../auth/access-token-provider";
4
+ import { Logger } from "../logger";
5
+ import { SmartlingException } from "../exception/index";
6
+ import { ResponseBodyType } from "../base/enum/response-body-type";
7
+ import { TranslatedFileDto } from "../dto/translated-file-dto";
8
+ import { ImportTmxFileParameters } from "./params/import-tmx-file-parameters";
9
+ import { TmxImportSummaryDto } from "./dto/tmx-import-summary-dto";
10
+ import { CreateStringSearchFilterParameters } from "./params/create-string-search-filter-parameters";
11
+ import { CreateStringSearchResultDto } from "./dto/create-string-search-result-dto";
12
+ import { BulkExportTmStringsParameters } from "./params/bulk-export-tm-strings-parameters";
13
+ import { AsyncActionResultDto } from "./dto/async-action-result-dto";
14
+ import { StringsExportAsyncActionResultDto } from "./dto/strings-export-async-action-result-dto";
15
+ import { CreateTmParameters } from "./params/create-tm-parameters";
16
+ import { UpdateTmParameters } from "./params/update-tm-parameters";
17
+ import { TmDto } from "./dto/tm-dto";
18
+
19
+ const TM_FIELDS = `
20
+ tmUid
21
+ name
22
+ description
23
+ sourceLocaleId
24
+ sourceLocale {
25
+ localeId
26
+ localeDescription
27
+ rtlLanguage
28
+ wordSpacing
29
+ }
30
+ creator {
31
+ userUid
32
+ name
33
+ }
34
+ created
35
+ lastModifier {
36
+ userUid
37
+ name
38
+ }
39
+ lastModified
40
+ localeSummary {
41
+ translationLocaleId
42
+ translationLocale {
43
+ localeId
44
+ localeDescription
45
+ rtlLanguage
46
+ wordSpacing
47
+ }
48
+ stringCount
49
+ wordCount
50
+ }
51
+ translationQuality
52
+ managedWordsEnabled
53
+ `;
54
+
55
+ const CREATE_TM_QUERY = `
56
+ mutation createTm($createTm: CreateTm!) {
57
+ createTm(createTm: $createTm) {
58
+ ${TM_FIELDS}
59
+ }
60
+ }
61
+ `;
62
+
63
+ const UPDATE_TM_QUERY = `
64
+ mutation updateTm($updateTm: UpdateTm!) {
65
+ updateTm(updateTm: $updateTm) {
66
+ ${TM_FIELDS}
67
+ }
68
+ }
69
+ `;
70
+
71
+ const DELETE_TM_QUERY = `
72
+ mutation deleteTm($deleteTm: DeleteTm!) {
73
+ deleteTm(deleteTm: $deleteTm) {
74
+ ${TM_FIELDS}
75
+ }
76
+ }
77
+ `;
78
+
79
+ const CREATE_STRING_SEARCH_QUERY = `
80
+ mutation createStringSearch($createStringSearchFilter: CreateStringSearchFilter!) {
81
+ createStringSearch(createStringSearchFilter: $createStringSearchFilter) {
82
+ searchUid
83
+ stringCount
84
+ wordCount
85
+ duplicateStrings {
86
+ duplicateKey
87
+ count
88
+ srcText
89
+ srcLocale
90
+ translationLocale
91
+ }
92
+ duplicateSearch
93
+ actionUid
94
+ offlineReportUid
95
+ }
96
+ }
97
+ `;
98
+
99
+ const BULK_EXPORT_TM_STRINGS_QUERY = `
100
+ mutation bulkExportTmStrings($bulkExportTmStrings: BulkExportTmStrings!) {
101
+ bulkExportTmStrings(bulkExportTmStrings: $bulkExportTmStrings) {
102
+ actionUid
103
+ totalCount
104
+ processedCount
105
+ errorCount
106
+ executionComplete
107
+ actionStatus
108
+ }
109
+ }
110
+ `;
111
+
112
+ const BULK_EXPORT_TM_STRINGS_STATUS_QUERY = `
113
+ query bulkExportTmStringsStatus($actionUid: ID!) {
114
+ bulkExportTmStringsStatus(actionUid: $actionUid) {
115
+ actionUid
116
+ totalCount
117
+ processedCount
118
+ errorCount
119
+ executionComplete
120
+ tmxDownloadKey
121
+ expired
122
+ actionStatus
123
+ }
124
+ }
125
+ `;
126
+
127
+ export class SmartlingTranslationMemoryApi extends SmartlingBaseFileApi {
128
+ constructor(smartlingApiBaseUrl: string, authApi: AccessTokenProvider, logger: Logger) {
129
+ super(logger);
130
+ this.authApi = authApi;
131
+ this.entrypoint = smartlingApiBaseUrl;
132
+ }
133
+
134
+ /* eslint-disable-next-line class-methods-use-this */
135
+ alterRequestData(uri: string, opts: Record<string, unknown>): Record<string, unknown> {
136
+ // Guard against re-converting an already-built FormData body: makeRequest() calls
137
+ // alterRequestData() again on a 401 retry, reusing the same opts object.
138
+ if (!(opts.body instanceof FormData)
139
+ && uri.match(/tm-tmx-api\/v2\/accounts\/.*\/tmx\/file$/g)) {
140
+ const formData = new FormData();
141
+
142
+ Object.keys(opts.body).forEach((key) => {
143
+ if (key === "file" && Buffer.isBuffer(opts.body[key])) {
144
+ formData.append(key, opts.body[key], {
145
+ filename: (opts.body as Record<string, string>).fileUri
146
+ });
147
+ } else {
148
+ formData.append(key, opts.body[key]);
149
+ }
150
+ });
151
+
152
+ opts.headers["Content-Type"] = formData.getHeaders()["content-type"];
153
+ opts.body = formData;
154
+ }
155
+
156
+ return opts;
157
+ }
158
+
159
+ // A 401-triggered retry is not guaranteed byte-safe for a streamed upload
160
+ // (setFileFromLocalFilePath/setFileContent): the underlying Node stream is a one-shot
161
+ // resource, so if it was already partially or fully read by the first attempt, the retried
162
+ // request can go out empty or truncated. The underlying one-shot-stream problem is shared by
163
+ // every alterRequestData-based upload in this SDK (api/job-batches, api/context), not
164
+ // something specific to this method; use setFileContentFromBuffer for a retry-safe upload
165
+ // here (a Buffer can be re-read, and this file's alterRequestData guards against reusing an
166
+ // already-built FormData on retry). job-batches/context lack that FormData guard, so a
167
+ // buffer alone would not make their uploads retry-safe too.
168
+ async importTmxFile(
169
+ accountUid: string, params: ImportTmxFileParameters
170
+ ): Promise<TmxImportSummaryDto> {
171
+ const result: TmxImportSummaryDto = await this.makeRequest(
172
+ "post",
173
+ `${this.entrypoint}/tm-tmx-api/v2/accounts/${accountUid}/tmx/file`,
174
+ params.export()
175
+ );
176
+
177
+ if (result.translationMemoryImportErrors?.length || result.parsingErrors?.length) {
178
+ this.logger.warn(`importTmxFile completed with partial errors: ${JSON.stringify({
179
+ translationMemoryImportErrors: result.translationMemoryImportErrors,
180
+ parsingErrors: result.parsingErrors
181
+ })}`);
182
+ }
183
+
184
+ return result;
185
+ }
186
+
187
+ async downloadTmxFile(accountUid: string, tmxFileKey: string): Promise<TranslatedFileDto> {
188
+ const encodedTmxFileKey = encodeURIComponent(tmxFileKey);
189
+
190
+ return await SmartlingBaseFileApi.downloadResponseToTranslatedFileDto(
191
+ await this.makeRequest(
192
+ "get",
193
+ `${this.entrypoint}/tm-tmx-api/v2/accounts/${accountUid}/tmx/file/${encodedTmxFileKey}`,
194
+ null,
195
+ ResponseBodyType.RAW_RESPONSE
196
+ )
197
+ );
198
+ }
199
+
200
+ async createStringSearch(
201
+ accountUid: string, filter: CreateStringSearchFilterParameters
202
+ ): Promise<CreateStringSearchResultDto | null> {
203
+ return await this.executeGraphQl(
204
+ accountUid,
205
+ CREATE_STRING_SEARCH_QUERY,
206
+ { createStringSearchFilter: filter.export() },
207
+ "createStringSearch"
208
+ );
209
+ }
210
+
211
+ async bulkExportTmStrings(
212
+ accountUid: string, params: BulkExportTmStringsParameters
213
+ ): Promise<AsyncActionResultDto | null> {
214
+ return await this.executeGraphQl(
215
+ accountUid,
216
+ BULK_EXPORT_TM_STRINGS_QUERY,
217
+ { bulkExportTmStrings: params.export() },
218
+ "bulkExportTmStrings"
219
+ );
220
+ }
221
+
222
+ async getBulkExportTmStringsStatus(
223
+ accountUid: string, actionUid: string
224
+ ): Promise<StringsExportAsyncActionResultDto | null> {
225
+ return await this.executeGraphQl(
226
+ accountUid,
227
+ BULK_EXPORT_TM_STRINGS_STATUS_QUERY,
228
+ { actionUid },
229
+ "bulkExportTmStringsStatus"
230
+ );
231
+ }
232
+
233
+ async createTm(accountUid: string, params: CreateTmParameters): Promise<TmDto | null> {
234
+ return await this.executeGraphQl(
235
+ accountUid,
236
+ CREATE_TM_QUERY,
237
+ { createTm: params.export() },
238
+ "createTm"
239
+ );
240
+ }
241
+
242
+ async updateTm(accountUid: string, params: UpdateTmParameters): Promise<TmDto | null> {
243
+ return await this.executeGraphQl(
244
+ accountUid,
245
+ UPDATE_TM_QUERY,
246
+ { updateTm: params.export() },
247
+ "updateTm"
248
+ );
249
+ }
250
+
251
+ // deleteTm isn't documented on the public GraphQL reference site, but it exists in the real
252
+ // backend schema (tmm.graphqls) with the same non-internal-only doc comment as
253
+ // createTm/updateTm ("Deletes a translation memory"), so it's included here per
254
+ // source-code-wins reconciliation.
255
+ async deleteTm(accountUid: string, tmUid: string): Promise<TmDto | null> {
256
+ return await this.executeGraphQl(
257
+ accountUid,
258
+ DELETE_TM_QUERY,
259
+ { deleteTm: { tmUid } },
260
+ "deleteTm"
261
+ );
262
+ }
263
+
264
+ // makeRequest()'s own JSON handling assumes the REST envelope shape ({response: {data}}) and
265
+ // unwraps it automatically - a GraphQL response's {data, errors} envelope doesn't match that,
266
+ // so this method requests the raw Response via ResponseBodyType.RAW_RESPONSE and parses the
267
+ // GraphQL envelope itself instead, mirroring makeRequest's own error-handling shape as closely
268
+ // as the different envelope allows.
269
+ private async executeGraphQl<T>(
270
+ accountUid: string,
271
+ query: string,
272
+ variables: Record<string, unknown>,
273
+ operationName: string
274
+ ): Promise<T | null> {
275
+ const response: Response = await this.makeRequest(
276
+ "post",
277
+ `${this.entrypoint}/tm-graphql-api/v2/accounts/${accountUid}/graphql`,
278
+ JSON.stringify({ query, variables }),
279
+ ResponseBodyType.RAW_RESPONSE
280
+ );
281
+
282
+ const requestId = response.headers.get("x-sl-requestid");
283
+ let textResponse: string;
284
+ let jsonResponse: { data?: Record<string, unknown>; errors?: Array<unknown> };
285
+
286
+ try {
287
+ textResponse = await response.text();
288
+
289
+ // The date reviver only matches these bare key names, not a path - harmless today
290
+ // since no nested field in this domain's DTOs shares them, but worth keeping in mind
291
+ // if a future field reuses "created"/"lastModified" for a non-Date value.
292
+ const parsed = JSON.parse(textResponse, (key, value) => {
293
+ if ((key === "created" || key === "lastModified") && value) {
294
+ return new Date(value);
295
+ }
296
+
297
+ return value;
298
+ });
299
+
300
+ if (!parsed || typeof parsed !== "object") {
301
+ throw new SmartlingException(
302
+ `GraphQL response for operation "${operationName}" is not a JSON object`,
303
+ { statusCode: response.status, errorResponse: textResponse, requestId }
304
+ );
305
+ }
306
+
307
+ jsonResponse = parsed;
308
+ } catch (e) {
309
+ if (e instanceof SmartlingException) {
310
+ throw e;
311
+ }
312
+
313
+ this.logger.error(`Couldn't parse GraphQL response json: ${e.toString()}`);
314
+
315
+ throw new SmartlingException("Couldn't parse GraphQL response json", {
316
+ statusCode: response.status,
317
+ errorResponse: textResponse,
318
+ requestId
319
+ }, e);
320
+ }
321
+
322
+ // tmxDownloadKey is a bearer-style download token - redact it everywhere this response
323
+ // gets surfaced (the debug log below, and the error payload further down), not just one
324
+ // of the two.
325
+ const redactTmxDownloadKey = (key: string, value: unknown): unknown => (
326
+ key === "tmxDownloadKey" && value ? "[REDACTED]" : value
327
+ );
328
+ const redactedJsonResponse = JSON.parse(JSON.stringify(jsonResponse, redactTmxDownloadKey));
329
+
330
+ this.logger.debug(
331
+ `Received GraphQL response for operation "${operationName}": ${JSON.stringify(redactedJsonResponse)}`
332
+ );
333
+
334
+ if (jsonResponse.errors && !Array.isArray(jsonResponse.errors)) {
335
+ this.logger.warn(`GraphQL response for operation "${operationName}" has an unexpected "errors" shape: ${JSON.stringify(jsonResponse.errors)}`);
336
+ } else if (Array.isArray(jsonResponse.errors) && jsonResponse.errors.length > 0) {
337
+ const firstMessage = (jsonResponse.errors[0] as { message?: string })?.message;
338
+ const suffix = firstMessage ? `: ${firstMessage}` : "";
339
+
340
+ throw new SmartlingException(
341
+ `GraphQL request for operation "${operationName}" failed${suffix}`,
342
+ {
343
+ errors: jsonResponse.errors,
344
+ // Partial data can still be present alongside errors (a nested field-level
345
+ // resolver error doesn't necessarily mean the mutation's side effect - e.g.
346
+ // a TM actually being created - didn't happen), so surface it for recovery
347
+ // rather than discarding it. Redacted the same as the debug log above.
348
+ data: redactedJsonResponse.data,
349
+ statusCode: response.status,
350
+ requestId
351
+ }
352
+ );
353
+ }
354
+
355
+ if (!jsonResponse.data || !(operationName in jsonResponse.data)) {
356
+ throw new SmartlingException(
357
+ `GraphQL response for operation "${operationName}" is missing the expected field`,
358
+ { response: redactedJsonResponse, statusCode: response.status, requestId }
359
+ );
360
+ }
361
+
362
+ // The operation's result is itself nullable per the real GraphQL schema (none of this
363
+ // domain's Mutation/Query root fields are declared non-null) - a present-but-null value
364
+ // is a legitimate result, not a malformed response, so it's returned as-is rather than
365
+ // thrown. Only a genuinely absent key (checked above) is treated as malformed.
366
+ return jsonResponse.data[operationName] as T | null;
367
+ }
368
+ }
@@ -0,0 +1,30 @@
1
+ import { BaseParameters } from "../../parameters/index";
2
+ import { ExtendedExportMetadataType } from "./extended-export-metadata-type";
3
+
4
+ export class BulkExportTmStringsParameters extends BaseParameters {
5
+ setSearchUid(searchUid: string): BulkExportTmStringsParameters {
6
+ this.set("searchUid", searchUid);
7
+
8
+ return this;
9
+ }
10
+
11
+ setExtendedMetadata(extendedMetadata: boolean): BulkExportTmStringsParameters {
12
+ this.set("extendedMetadata", extendedMetadata);
13
+
14
+ return this;
15
+ }
16
+
17
+ setExtendedMetadataType(
18
+ extendedMetadataType: ExtendedExportMetadataType
19
+ ): BulkExportTmStringsParameters {
20
+ this.set("extendedMetadataType", extendedMetadataType);
21
+
22
+ return this;
23
+ }
24
+
25
+ setIncludeAllVariants(includeAllVariants: boolean): BulkExportTmStringsParameters {
26
+ this.set("includeAllVariants", includeAllVariants);
27
+
28
+ return this;
29
+ }
30
+ }