sanity-plugin-studio-smartling 4.3.2 → 5.0.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.
@@ -1,166 +0,0 @@
1
- import {authenticate, getHeaders, findExistingJob} from './helpers'
2
- import {Adapter, Secrets, SerializedDocument} from 'sanity-translations-tab'
3
- import {getTranslationTask} from './getTranslationTask'
4
- import {Buffer} from 'buffer'
5
-
6
- const createJob = (
7
- jobName: string,
8
- secrets: Secrets,
9
- localeIds: string[],
10
- accessToken: string,
11
- documentId: string,
12
- ) => {
13
- const {project, proxy} = secrets
14
- if (!project || !proxy) {
15
- throw new Error(
16
- 'The Smartling adapter requires a Smartling project identifier and a proxy URL. Please check your secrets document in this dataset, per the plugin documentation.',
17
- )
18
- }
19
-
20
- const url = `https://api.smartling.com/jobs-api/v3/projects/${project}/jobs`
21
- return fetch(proxy, {
22
- method: 'POST',
23
- headers: {
24
- ...getHeaders(url, accessToken),
25
- 'content-type': 'application/json',
26
- },
27
- body: JSON.stringify({
28
- jobName,
29
- targetLocaleIds: localeIds,
30
- referenceNumber: documentId,
31
- }),
32
- })
33
- .then((res) => res.json())
34
- .then((res) => res.response.data.translationJobUid)
35
- }
36
-
37
- /* we're using batches here because it eliminates some
38
- * new string authorization issues for updating existing jobs,
39
- * and is able to be used for new bulk
40
- * job functionality.
41
- */
42
-
43
- const createJobBatch = (
44
- jobId: string,
45
- secrets: Secrets,
46
- documentId: string,
47
- accessToken: string,
48
- localeIds: string[],
49
- workflowUid?: string,
50
- //eslint-disable-next-line max-params
51
- ) => {
52
- const {project, proxy} = secrets
53
- if (!project || !proxy) {
54
- throw new Error(
55
- 'The Smartling adapter requires a Smartling project identifier and a proxy URL. Please check your secrets document in this dataset, per the plugin documentation.',
56
- )
57
- }
58
- const url = `https://api.smartling.com/job-batches-api/v2/projects/${project}/batches`
59
- const reqBody: {
60
- authorize: boolean
61
- translationJobUid: string
62
- fileUris: string[]
63
- localeWorkflows?: {targetLocaleId: string; workflowUid: string}[]
64
- } = {
65
- authorize: true,
66
- translationJobUid: jobId,
67
- fileUris: [documentId],
68
- }
69
-
70
- if (workflowUid) {
71
- reqBody.localeWorkflows = localeIds.map((l) => ({
72
- targetLocaleId: l,
73
- workflowUid,
74
- }))
75
- }
76
-
77
- return fetch(proxy, {
78
- method: 'POST',
79
- headers: {
80
- ...getHeaders(url, accessToken),
81
- 'content-type': 'application/json',
82
- },
83
- body: JSON.stringify(reqBody),
84
- })
85
- .then((res) => res.json())
86
- .then((res) => res.response.data.batchUid)
87
- }
88
-
89
- const uploadFileToBatch = (
90
- batchUid: string,
91
- documentId: string,
92
- document: SerializedDocument,
93
- secrets: Secrets,
94
- localeIds: string[],
95
- accessToken: string,
96
- callbackUrl?: string,
97
- //eslint-disable-next-line max-params
98
- ) => {
99
- const {project, proxy} = secrets
100
- if (!project || !proxy) {
101
- throw new Error(
102
- 'The Smartling adapter requires a Smartling project identifier and a proxy URL. Please check your secrets document in this dataset, per the plugin documentation.',
103
- )
104
- }
105
- const url = `https://api.smartling.com/job-batches-api/v2/projects/${project}/batches/${batchUid}/file`
106
- const formData = new FormData()
107
- formData.append('fileUri', documentId)
108
- formData.append('fileType', 'html')
109
- const htmlBuffer = Buffer.from(document.content, 'utf-8')
110
- formData.append('file', new Blob([htmlBuffer]), `${document.name}.html`)
111
- localeIds.forEach((localeId) => formData.append('localeIdsToAuthorize[]', localeId))
112
- if (callbackUrl) {
113
- formData.append('callbackUrl', callbackUrl)
114
- }
115
-
116
- return fetch(proxy, {
117
- method: 'POST',
118
- headers: getHeaders(url, accessToken),
119
- body: formData,
120
- }).then((res) => res.json())
121
- }
122
-
123
- export const createTask: Adapter['createTask'] = async (
124
- documentId: string,
125
- document: SerializedDocument,
126
- localeIds: string[],
127
- secrets: Secrets | null,
128
- workflowUid?: string,
129
- callbackUrl?: string,
130
- // eslint-disable-next-line max-params
131
- ) => {
132
- if (!secrets?.project || !secrets?.secret || !secrets?.proxy) {
133
- throw new Error(
134
- 'The Smartling adapter requires a project ID, a secret key, and a proxy URL. Please check your secrets document in this dataset, per the plugin documentation.',
135
- )
136
- }
137
-
138
- const accessToken = await authenticate(secrets)
139
-
140
- let taskId = await findExistingJob(document.name, secrets, accessToken)
141
- if (!taskId) {
142
- taskId = await createJob(document.name, secrets, localeIds, accessToken, documentId)
143
- }
144
-
145
- const batchUid = await createJobBatch(
146
- taskId,
147
- secrets,
148
- documentId,
149
- accessToken,
150
- localeIds,
151
- workflowUid,
152
- )
153
- const uploadFileRes = await uploadFileToBatch(
154
- batchUid,
155
- documentId,
156
- document,
157
- secrets,
158
- localeIds,
159
- accessToken,
160
- callbackUrl,
161
- )
162
- //eslint-disable-next-line no-console -- for developer debugging
163
- console.info('Upload status from Smartling: ', uploadFileRes)
164
-
165
- return getTranslationTask(documentId, secrets)
166
- }
@@ -1,18 +0,0 @@
1
- import {authenticate, getHeaders} from './helpers'
2
- import {Secrets} from 'sanity-translations-tab'
3
- import {Adapter} from 'sanity-translations-tab'
4
-
5
- export const getLocales: Adapter['getLocales'] = async (secrets: Secrets | null) => {
6
- if (!secrets?.project || !secrets?.secret || !secrets?.proxy) {
7
- return []
8
- }
9
- const {project, proxy} = secrets
10
- const url = `https://api.smartling.com/projects-api/v2/projects/${project}`
11
- const accessToken = await authenticate(secrets)
12
- return fetch(proxy, {
13
- method: 'GET',
14
- headers: getHeaders(url, accessToken),
15
- })
16
- .then((res) => res.json())
17
- .then((res) => res.response.data.targetLocales)
18
- }
@@ -1,36 +0,0 @@
1
- import {authenticate, getHeaders} from './helpers'
2
- import {Adapter, Secrets} from 'sanity-translations-tab'
3
-
4
- export const getTranslation: Adapter['getTranslation'] = async (
5
- taskId: string,
6
- localeId: string,
7
- secrets: Secrets | null,
8
- ) => {
9
- if (!secrets?.project || !secrets?.secret || !secrets?.proxy) {
10
- throw new Error(
11
- 'The Smartling adapter requires a project ID, a secret key, and a proxy URL. Please check your secrets document in this dataset, per the plugin documentation.',
12
- )
13
- }
14
-
15
- const {project, proxy} = secrets
16
-
17
- const url = `https://api.smartling.com/files-api/v2/projects/${project}/locales/${localeId}/file?fileUri=${taskId}&retrievalType=pending`
18
- const accessToken = await authenticate(secrets)
19
- const translatedHTML = await fetch(proxy, {
20
- method: 'GET',
21
- headers: getHeaders(url, accessToken),
22
- })
23
- .then((res) => res.json())
24
- .then((res) => {
25
- if (res.body) {
26
- return res.body
27
- } else if (res.response.errors) {
28
- const errMsg =
29
- res.response.errors[0]?.message || 'Error retrieving translation from Smartling'
30
- throw new Error(errMsg)
31
- }
32
- return ''
33
- })
34
-
35
- return translatedHTML
36
- }
@@ -1,89 +0,0 @@
1
- import {authenticate, getHeaders, findExistingJob} from './helpers'
2
- import {Adapter, Secrets} from 'sanity-translations-tab'
3
-
4
- interface WorkflowProgressItem {
5
- workflowStepSummaryReportItemList: {
6
- wordCount: number
7
- }[]
8
- }
9
-
10
- interface SmartlingProgressItem {
11
- targetLocaleId: string
12
- progress: {
13
- percentComplete: number
14
- totalWordCount: number
15
- }
16
- workflowProgressReportList: WorkflowProgressItem[]
17
- }
18
-
19
- export const getTranslationTask: Adapter['getTranslationTask'] = async (
20
- documentId: string,
21
- secrets: Secrets | null,
22
- ) => {
23
- if (!secrets?.project || !secrets?.secret || !secrets?.proxy) {
24
- return {
25
- documentId,
26
- taskId: documentId,
27
- locales: [],
28
- }
29
- }
30
-
31
- const {project, proxy} = secrets
32
-
33
- const accessToken = await authenticate(secrets)
34
- const taskId = await findExistingJob(documentId, secrets, accessToken)
35
- if (!taskId) {
36
- return {
37
- documentId,
38
- taskId: documentId,
39
- locales: [],
40
- }
41
- }
42
-
43
- const progressUrl = `https://api.smartling.com/jobs-api/v3/projects/${project}/jobs/${taskId}/progress`
44
- const smartlingTask = await fetch(proxy, {
45
- method: 'GET',
46
- headers: getHeaders(progressUrl, accessToken),
47
- })
48
- .then((res) => res.json())
49
- .then((res) => res.response.data)
50
-
51
- let locales = []
52
- if (smartlingTask && smartlingTask.contentProgressReport) {
53
- locales = smartlingTask.contentProgressReport.map((item: SmartlingProgressItem) => {
54
- let progress = item.progress ? item.progress.percentComplete : 0
55
- if (
56
- item.workflowProgressReportList &&
57
- item.workflowProgressReportList.length > 0 &&
58
- item.progress
59
- ) {
60
- //default to the first workflow -- it's likely what is being used
61
- const progressItem = item.workflowProgressReportList[0]
62
- //this is a list of the various steps in the workflow
63
- if (
64
- progressItem.workflowStepSummaryReportItemList &&
65
- progressItem.workflowStepSummaryReportItemList.length > 1
66
- ) {
67
- //get the last step in the workflow -- usually "published"
68
- const lastStep = progressItem.workflowStepSummaryReportItemList.at(-1)
69
- //get the percentage of how many words have reached the last step
70
- if (lastStep && lastStep.wordCount >= 0) {
71
- progress = Math.floor((lastStep.wordCount / item.progress.totalWordCount) * 100) ?? 0
72
- }
73
- }
74
- }
75
- return {
76
- localeId: item.targetLocaleId,
77
- progress,
78
- }
79
- })
80
- }
81
-
82
- return {
83
- documentId,
84
- locales,
85
- //since our download is tied to document id for smartling, keep track of it as a task
86
- taskId: documentId,
87
- linkToVendorTask: `https://dashboard.smartling.com/app/projects/${project}/account-jobs/${project}:${taskId}`,
88
- }
89
- }
@@ -1,84 +0,0 @@
1
- import {Secrets} from 'sanity-translations-tab'
2
-
3
- interface Headers {
4
- [key: string]: string
5
- }
6
-
7
- export const authenticate = (secrets: Secrets): Promise<string> => {
8
- const url = 'https://api.smartling.com/auth-api/v2/authenticate'
9
- const headers = {
10
- 'content-type': 'application/json',
11
- 'X-URL': url,
12
- }
13
- const {secret, proxy} = secrets
14
- if (!secret || !proxy) {
15
- throw new Error(
16
- 'The Smartling adapter requires a secret key and a proxy URL. Please check your secrets document in this dataset, per the plugin documentation.',
17
- )
18
- }
19
- return fetch(proxy, {
20
- headers,
21
- method: 'POST',
22
- body: JSON.stringify(secret),
23
- })
24
- .then((res) => res.json())
25
- .then((res) => res.response.data.accessToken)
26
- }
27
-
28
- export const getHeaders = (url: string, accessToken: string): Headers => ({
29
- Authorization: `Bearer ${accessToken}`,
30
- 'X-URL': url,
31
- })
32
-
33
- export const findExistingJob = async (
34
- documentId: string,
35
- secrets: Secrets,
36
- accessToken: string,
37
- ): Promise<string> => {
38
- const {project, proxy} = secrets
39
- if (!project || !proxy) {
40
- throw new Error(
41
- 'The Smartling adapter requires a Smartling project identifier and a proxy URL. Please check your secrets document in this dataset, per the plugin documentation.',
42
- )
43
- }
44
- const url = `https://api.smartling.com/jobs-api/v3/projects/${project}/jobs?jobName=${documentId}`
45
- //first, try fetching from name resolution
46
- let items = await fetch(proxy, {
47
- headers: getHeaders(url, accessToken),
48
- })
49
- .then((res) => res.json())
50
- .then((res) => res?.response?.data?.items)
51
-
52
- if (!items || !items.length) {
53
- //if that fails, try fetching by fileUri and check the referenceNumber
54
- const refUrl = `https://api.smartling.com/jobs-api/v3/projects/${project}/jobs/search`
55
- items = await fetch(proxy, {
56
- headers: {
57
- ...getHeaders(refUrl, accessToken),
58
- 'content-type': 'application/json',
59
- },
60
- method: 'POST',
61
- body: JSON.stringify({
62
- fileUris: [documentId],
63
- }),
64
- })
65
- .then((res) => res.json())
66
- .then((res) => res?.response?.data?.items)
67
- }
68
-
69
- if (items.length) {
70
- //smartling will fuzzy match job names. We need to be precise.
71
- const correctJob = items
72
- .filter((item: {jobStatus: string}) => item.jobStatus !== 'DELETED')
73
- .find(
74
- (item: {jobName: string; referenceNumber: string}) =>
75
- (item.jobName && item.jobName === documentId) ||
76
- (item.referenceNumber && item.referenceNumber === documentId),
77
- )
78
-
79
- if (correctJob) {
80
- return correctJob.translationJobUid
81
- }
82
- }
83
- return ''
84
- }
@@ -1,12 +0,0 @@
1
- import {Adapter} from 'sanity-translations-tab'
2
- import {getLocales} from './getLocales'
3
- import {getTranslationTask} from './getTranslationTask'
4
- import {createTask} from './createTask'
5
- import {getTranslation} from './getTranslation'
6
-
7
- export const SmartlingAdapter: Adapter = {
8
- getLocales,
9
- getTranslationTask,
10
- createTask,
11
- getTranslation,
12
- }
package/src/index.ts DELETED
@@ -1,52 +0,0 @@
1
- import {
2
- TranslationsTab,
3
- baseDocumentLevelConfig,
4
- legacyDocumentLevelConfig as baseLegacyDocumentLevelConfig,
5
- baseFieldLevelConfig,
6
- findLatestDraft,
7
- BaseDocumentDeserializer,
8
- BaseDocumentSerializer,
9
- BaseDocumentMerger,
10
- defaultStopTypes,
11
- customSerializers,
12
- legacyDocumentLevelPatch,
13
- documentLevelPatch,
14
- fieldLevelPatch,
15
- TranslationFunctionContext,
16
- TranslationsTabConfigOptions,
17
- } from 'sanity-translations-tab'
18
- import {SmartlingAdapter} from './adapter'
19
-
20
- const defaultDocumentLevelConfig: TranslationsTabConfigOptions = {
21
- ...baseDocumentLevelConfig,
22
- adapter: SmartlingAdapter,
23
- }
24
-
25
- const legacyDocumentLevelConfig: TranslationsTabConfigOptions = {
26
- ...baseLegacyDocumentLevelConfig,
27
- adapter: SmartlingAdapter,
28
- }
29
-
30
- const defaultFieldLevelConfig: TranslationsTabConfigOptions = {
31
- ...baseFieldLevelConfig,
32
- adapter: SmartlingAdapter,
33
- }
34
-
35
- export {
36
- TranslationsTab,
37
- findLatestDraft,
38
- legacyDocumentLevelPatch,
39
- documentLevelPatch,
40
- fieldLevelPatch,
41
- BaseDocumentDeserializer,
42
- BaseDocumentSerializer,
43
- BaseDocumentMerger,
44
- defaultStopTypes,
45
- customSerializers,
46
- SmartlingAdapter,
47
- legacyDocumentLevelConfig,
48
- defaultDocumentLevelConfig,
49
- defaultFieldLevelConfig,
50
- }
51
-
52
- export type {TranslationFunctionContext, TranslationsTabConfigOptions}
package/src/types.d.ts DELETED
File without changes
@@ -1,11 +0,0 @@
1
- const {showIncompatiblePluginDialog} = require('@sanity/incompatible-plugin')
2
- const {name, version, sanityExchangeUrl} = require('./package.json')
3
-
4
- export default showIncompatiblePluginDialog({
5
- name: name,
6
- versions: {
7
- v3: version,
8
- v2: undefined,
9
- },
10
- sanityExchangeUrl,
11
- })