sanity-plugin-studio-smartling 2.0.5 → 3.0.0-beta

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,20 +1,18 @@
1
- import {
2
- smartlingProxy,
3
- authenticate,
4
- getHeaders,
5
- findExistingJob,
6
- } from './helpers'
7
- import { Secrets } from 'sanity-translations-tab'
8
- import { getTranslationTask } from './getTranslationTask'
1
+ import {authenticate, getHeaders, findExistingJob} from './helpers'
2
+ import {Adapter, Secrets} from 'sanity-translations-tab'
3
+ import {getTranslationTask} from './getTranslationTask'
4
+ import {Buffer} from 'buffer'
9
5
 
10
- const createJob = async (
11
- jobName: string,
12
- projectId: string,
13
- localeIds: string[],
14
- accessToken: string
15
- ) => {
16
- const url = `https://api.smartling.com/jobs-api/v3/projects/${projectId}/jobs`
17
- return fetch(smartlingProxy, {
6
+ const createJob = (jobName: string, secrets: Secrets, localeIds: string[], accessToken: string) => {
7
+ const {project, proxy} = secrets
8
+ if (!project || !proxy) {
9
+ throw new Error(
10
+ 'The Smartling adapter requires a Smartling project identifier and a proxy URL. Please check your secrets document in this dataset, per the plugin documentation.'
11
+ )
12
+ }
13
+
14
+ const url = `https://api.smartling.com/jobs-api/v3/projects/${project}/jobs`
15
+ return fetch(proxy, {
18
16
  method: 'POST',
19
17
  headers: {
20
18
  ...getHeaders(url, accessToken),
@@ -25,8 +23,8 @@ const createJob = async (
25
23
  targetLocaleIds: localeIds,
26
24
  }),
27
25
  })
28
- .then(res => res.json())
29
- .then(res => res.response.data.translationJobUid)
26
+ .then((res) => res.json())
27
+ .then((res) => res.response.data.translationJobUid)
30
28
  }
31
29
 
32
30
  /* we're using batches here because it eliminates some
@@ -34,20 +32,28 @@ const createJob = async (
34
32
  * and is able to be used for new bulk
35
33
  * job functionality.
36
34
  */
37
- const createJobBatch = async (
35
+
36
+ const createJobBatch = (
38
37
  jobId: string,
39
- projectId: string,
38
+ secrets: Secrets,
40
39
  documentName: string,
41
40
  accessToken: string,
42
41
  localeIds: string[],
43
42
  workflowUid?: string
43
+ //eslint-disable-next-line max-params
44
44
  ) => {
45
- const url = `https://api.smartling.com/job-batches-api/v2/projects/${projectId}/batches`
45
+ const {project, proxy} = secrets
46
+ if (!project || !proxy) {
47
+ throw new Error(
48
+ 'The Smartling adapter requires a Smartling project identifier and a proxy URL. Please check your secrets document in this dataset, per the plugin documentation.'
49
+ )
50
+ }
51
+ const url = `https://api.smartling.com/job-batches-api/v2/projects/${project}/batches`
46
52
  const reqBody: {
47
53
  authorize: boolean
48
54
  translationJobUid: string
49
55
  fileUris: string[]
50
- localeWorkflows?: { targetLocaleId: string; workflowUid: string }[]
56
+ localeWorkflows?: {targetLocaleId: string; workflowUid: string}[]
51
57
  } = {
52
58
  authorize: true,
53
59
  translationJobUid: jobId,
@@ -55,13 +61,13 @@ const createJobBatch = async (
55
61
  }
56
62
 
57
63
  if (workflowUid) {
58
- reqBody.localeWorkflows = localeIds.map(l => ({
64
+ reqBody.localeWorkflows = localeIds.map((l) => ({
59
65
  targetLocaleId: l,
60
66
  workflowUid,
61
67
  }))
62
68
  }
63
69
 
64
- return fetch(smartlingProxy, {
70
+ return fetch(proxy, {
65
71
  method: 'POST',
66
72
  headers: {
67
73
  ...getHeaders(url, accessToken),
@@ -69,75 +75,70 @@ const createJobBatch = async (
69
75
  },
70
76
  body: JSON.stringify(reqBody),
71
77
  })
72
- .then(res => res.json())
73
- .then(res => res.response.data.batchUid)
78
+ .then((res) => res.json())
79
+ .then((res) => res.response.data.batchUid)
74
80
  }
75
81
 
76
- const uploadFileToBatch = async (
82
+ const uploadFileToBatch = (
77
83
  batchUid: string,
78
84
  document: Record<string, any>,
79
- projectId: string,
85
+ secrets: Secrets,
80
86
  localeIds: string[],
81
87
  accessToken: string
82
88
  ) => {
83
- const url = `https://api.smartling.com/job-batches-api/v2/projects/${projectId}/batches/${batchUid}/file`
89
+ const {project, proxy} = secrets
90
+ if (!project || !proxy) {
91
+ throw new Error(
92
+ 'The Smartling adapter requires a Smartling project identifier and a proxy URL. Please check your secrets document in this dataset, per the plugin documentation.'
93
+ )
94
+ }
95
+ const url = `https://api.smartling.com/job-batches-api/v2/projects/${project}/batches/${batchUid}/file`
84
96
  const formData = new FormData()
85
97
  formData.append('fileUri', document.name)
86
98
  formData.append('fileType', 'html')
87
99
  const htmlBuffer = Buffer.from(document.content, 'utf-8')
88
100
  formData.append('file', new Blob([htmlBuffer]), `${document.name}.html`)
89
- localeIds.forEach(localeId =>
90
- formData.append('localeIdsToAuthorize[]', localeId)
91
- )
101
+ localeIds.forEach((localeId) => formData.append('localeIdsToAuthorize[]', localeId))
92
102
 
93
- return fetch(smartlingProxy, {
103
+ return fetch(proxy, {
94
104
  method: 'POST',
95
105
  headers: getHeaders(url, accessToken),
96
106
  body: formData,
97
- }).then(res => res.json())
107
+ }).then((res) => res.json())
98
108
  }
99
109
 
100
- export const createTask = async (
110
+ //@ts-ignore until return TranslationTask type is added to sanity-translations-tab
111
+ export const createTask: Adapter['createTask'] = async (
101
112
  documentId: string,
102
113
  document: Record<string, any>,
103
114
  localeIds: string[],
104
- secrets: Secrets,
115
+ secrets: Secrets | null,
105
116
  workflowUid?: string
106
117
  ) => {
107
- const accessToken = await authenticate(secrets.secret)
118
+ if (!secrets?.project || !secrets?.secret || !secrets?.proxy) {
119
+ throw new Error(
120
+ '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.'
121
+ )
122
+ }
108
123
 
109
- //TODO: announce errors here
110
- let taskId = await findExistingJob(
111
- document.name,
112
- secrets.project,
113
- accessToken
114
- )
124
+ const accessToken = await authenticate(secrets)
125
+
126
+ let taskId = await findExistingJob(document.name, secrets, accessToken)
115
127
  if (!taskId) {
116
- taskId = await createJob(
117
- document.name,
118
- secrets.project,
119
- localeIds,
120
- accessToken
121
- )
128
+ taskId = await createJob(document.name, secrets, localeIds, accessToken)
122
129
  }
123
130
 
124
- //TODO: log errors here if needed
125
131
  const batchUid = await createJobBatch(
126
132
  taskId,
127
- secrets.project,
133
+ secrets,
128
134
  document.name,
129
135
  accessToken,
130
136
  localeIds,
131
137
  workflowUid
132
138
  )
133
- const uploadFileRes = await uploadFileToBatch(
134
- batchUid,
135
- document,
136
- secrets.project,
137
- localeIds,
138
- accessToken
139
- )
140
- console.log('upload status', uploadFileRes)
139
+ const uploadFileRes = await uploadFileToBatch(batchUid, document, secrets, localeIds, accessToken)
140
+ //eslint-disable-next-line no-console -- for developer debugging
141
+ console.info('Upload status from Smartling: ', uploadFileRes)
141
142
 
142
143
  return getTranslationTask(documentId, secrets)
143
144
  }
@@ -1,13 +1,20 @@
1
- import { smartlingProxy, authenticate, getHeaders } from './helpers'
2
- import { Secrets } from 'sanity-translations-tab'
1
+ import {authenticate, getHeaders} from './helpers'
2
+ import {Secrets} from 'sanity-translations-tab'
3
+ import {Adapter} from 'sanity-translations-tab'
3
4
 
4
- export const getLocales = async (secrets: Secrets) => {
5
- const url = `https://api.smartling.com/projects-api/v2/projects/${secrets.project}`
6
- const accessToken = await authenticate(secrets.secret)
7
- return fetch(smartlingProxy, {
8
- method: 'POST',
5
+ export const getLocales: Adapter['getLocales'] = async (secrets: Secrets | null) => {
6
+ if (!secrets?.project || !secrets?.secret || !secrets?.proxy) {
7
+ throw new Error(
8
+ '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.'
9
+ )
10
+ }
11
+ const {project, proxy} = secrets
12
+ const url = `https://api.smartling.com/projects-api/v2/projects/${project}`
13
+ const accessToken = await authenticate(secrets)
14
+ return fetch(proxy, {
15
+ method: 'GET',
9
16
  headers: getHeaders(url, accessToken),
10
17
  })
11
- .then(res => res.json())
12
- .then(res => res.response.data.targetLocales)
18
+ .then((res) => res.json())
19
+ .then((res) => res.response.data.targetLocales)
13
20
  }
@@ -1,27 +1,35 @@
1
- import { smartlingProxy, authenticate, getHeaders } from './helpers'
2
- import { Secrets } from 'sanity-translations-tab'
1
+ import {authenticate, getHeaders} from './helpers'
2
+ import {Adapter, Secrets} from 'sanity-translations-tab'
3
3
 
4
- export const getTranslation = async (
4
+ export const getTranslation: Adapter['getTranslation'] = async (
5
5
  taskId: string,
6
6
  localeId: string,
7
- secrets: Secrets
7
+ secrets: Secrets | null
8
8
  ) => {
9
- const url = `https://api.smartling.com/files-api/v2/projects/${secrets.project}/locales/${localeId}/file?fileUri=${taskId}&retrievalType=pending`
10
- const accessToken = await authenticate(secrets.secret)
11
- const translatedHTML = await fetch(smartlingProxy, {
12
- method: 'POST',
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',
13
21
  headers: getHeaders(url, accessToken),
14
22
  })
15
- .then(res => res.json())
16
- .then(res => {
23
+ .then((res) => res.json())
24
+ .then((res) => {
17
25
  if (res.body) {
18
26
  return res.body
19
27
  } else if (res.response.errors) {
20
28
  const errMsg =
21
- res.response.errors[0]?.message ||
22
- 'Error retrieving translation from Smartling'
29
+ res.response.errors[0]?.message || 'Error retrieving translation from Smartling'
23
30
  throw new Error(errMsg)
24
31
  }
32
+ return ''
25
33
  })
26
34
 
27
35
  return translatedHTML
@@ -1,17 +1,27 @@
1
- import {
2
- smartlingProxy,
3
- authenticate,
4
- getHeaders,
5
- findExistingJob,
6
- } from './helpers'
7
- import { Secrets } from 'sanity-translations-tab'
1
+ import {authenticate, getHeaders, findExistingJob} from './helpers'
2
+ import {Adapter, Secrets} from 'sanity-translations-tab'
8
3
 
9
- export const getTranslationTask = async (
4
+ interface SmartlingProgressItem {
5
+ targetLocaleId: string
6
+ progress: {
7
+ percentComplete: number
8
+ }
9
+ }
10
+
11
+ export const getTranslationTask: Adapter['getTranslationTask'] = async (
10
12
  documentId: string,
11
- secrets: Secrets
13
+ secrets: Secrets | null
12
14
  ) => {
13
- const accessToken = await authenticate(secrets.secret)
14
- const taskId = await findExistingJob(documentId, secrets.project, accessToken)
15
+ if (!secrets?.project || !secrets?.secret || !secrets?.proxy) {
16
+ throw new Error(
17
+ '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.'
18
+ )
19
+ }
20
+
21
+ const {project, proxy} = secrets
22
+
23
+ const accessToken = await authenticate(secrets)
24
+ const taskId = await findExistingJob(documentId, secrets, accessToken)
15
25
  if (!taskId) {
16
26
  return {
17
27
  documentId,
@@ -20,18 +30,17 @@ export const getTranslationTask = async (
20
30
  }
21
31
  }
22
32
 
23
- const projectId = secrets.project
24
- const progressUrl = `https://api.smartling.com/jobs-api/v3/projects/${projectId}/jobs/${taskId}/progress`
25
- const smartlingTask = await fetch(smartlingProxy, {
26
- method: 'POST',
33
+ const progressUrl = `https://api.smartling.com/jobs-api/v3/projects/${project}/jobs/${taskId}/progress`
34
+ const smartlingTask = await fetch(proxy, {
35
+ method: 'GET',
27
36
  headers: getHeaders(progressUrl, accessToken),
28
37
  })
29
- .then(res => res.json())
30
- .then(res => res.response.data)
38
+ .then((res) => res.json())
39
+ .then((res) => res.response.data)
31
40
 
32
41
  let locales = []
33
42
  if (smartlingTask && smartlingTask.contentProgressReport) {
34
- locales = smartlingTask.contentProgressReport.map(item => ({
43
+ locales = smartlingTask.contentProgressReport.map((item: SmartlingProgressItem) => ({
35
44
  localeId: item.targetLocaleId,
36
45
  progress: item.progress ? item.progress.percentComplete : 0,
37
46
  }))
@@ -42,6 +51,6 @@ export const getTranslationTask = async (
42
51
  locales,
43
52
  //since our download is tied to document id for smartling, keep track of it as a task
44
53
  taskId: documentId,
45
- linkToVendorTask: `https://dashboard.smartling.com/app/projects/${projectId}/account-jobs/${projectId}:${taskId}`,
54
+ linkToVendorTask: `https://dashboard.smartling.com/app/projects/${project}/account-jobs/${project}:${taskId}`,
46
55
  }
47
56
  }
@@ -1,49 +1,63 @@
1
- export const smartlingProxy = process.env.SANITY_STUDIO_SMARTLING_PROXY
1
+ import {Secrets} from 'sanity-translations-tab'
2
2
 
3
- export const authenticate = async (secret: string) => {
3
+ interface Headers {
4
+ [key: string]: string
5
+ }
6
+
7
+ export const authenticate = (secrets: Secrets): Promise<string> => {
4
8
  const url = 'https://api.smartling.com/auth-api/v2/authenticate'
5
9
  const headers = {
6
10
  'content-type': 'application/json',
7
11
  'X-URL': url,
8
12
  }
9
- return fetch(smartlingProxy, {
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, {
10
20
  headers,
11
21
  method: 'POST',
12
22
  body: JSON.stringify(secret),
13
23
  })
14
- .then(res => res.json())
15
- .then(res => res.response.data.accessToken)
24
+ .then((res) => res.json())
25
+ .then((res) => res.response.data.accessToken)
16
26
  }
17
27
 
18
- export const getHeaders = (url: string, accessToken: string) => ({
28
+ export const getHeaders = (url: string, accessToken: string): Headers => ({
19
29
  Authorization: `Bearer ${accessToken}`,
20
30
  'X-URL': url,
21
31
  })
22
32
 
23
- export const findExistingJob = async (
33
+ export const findExistingJob = (
24
34
  documentId: string,
25
- projectId: string,
35
+ secrets: Secrets,
26
36
  accessToken: string
27
37
  ): Promise<string> => {
28
- const url = `https://api.smartling.com/jobs-api/v3/projects/${projectId}/jobs?jobName=${documentId}`
29
- return fetch(smartlingProxy, {
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
+ return fetch(proxy, {
30
46
  method: 'POST',
31
47
  headers: getHeaders(url, accessToken),
32
48
  })
33
- .then(res => res.json())
34
- .then(res => {
49
+ .then((res) => res.json())
50
+ .then((res) => {
35
51
  if (res.response.data.items.length) {
36
52
  //smartling will fuzzy match job names. We need to be precise.
37
53
  const correctJob = res.response.data.items.find(
38
- item => item.jobName && item.jobName === documentId
54
+ (item: {jobName: string}) => item.jobName && item.jobName === documentId
39
55
  )
40
56
  if (correctJob) {
41
57
  return correctJob.translationJobUid
42
- } else {
43
- return ''
44
58
  }
45
- } else {
46
59
  return ''
47
60
  }
61
+ return ''
48
62
  })
49
63
  }
@@ -1,8 +1,8 @@
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'
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
6
 
7
7
  export const SmartlingAdapter: Adapter = {
8
8
  getLocales,
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import {SerializedDocument} from 'sanity-naive-html-serializer'
1
2
  import {
2
3
  TranslationsTab,
3
4
  baseDocumentLevelConfig,
@@ -11,17 +12,22 @@ import {
11
12
  Adapter,
12
13
  documentLevelPatch,
13
14
  fieldLevelPatch,
15
+ TranslationFunctionContext,
14
16
  } from 'sanity-translations-tab'
15
- import { SmartlingAdapter } from './adapter'
17
+ import {SmartlingAdapter} from './adapter'
16
18
 
17
19
  interface ConfigOptions {
18
20
  adapter: Adapter
19
21
  secretsNamespace: string | null
20
- exportForTranslation: (id: string) => Promise<Record<string, any>>
22
+ exportForTranslation: (
23
+ id: string,
24
+ context: TranslationFunctionContext
25
+ ) => Promise<SerializedDocument>
21
26
  importTranslation: (
22
27
  id: string,
23
28
  localeId: string,
24
- doc: string
29
+ doc: string,
30
+ context: TranslationFunctionContext
25
31
  ) => Promise<void>
26
32
  }
27
33
  const defaultDocumentLevelConfig: ConfigOptions = {
package/src/types.d.ts ADDED
File without changes
@@ -0,0 +1,11 @@
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
+ })
@@ -1,12 +0,0 @@
1
- import { Secrets } from 'sanity-translations-tab';
2
- export declare const createTask: (documentId: string, document: Record<string, any>, localeIds: string[], secrets: Secrets, workflowUid?: string) => Promise<{
3
- documentId: string;
4
- taskId: string;
5
- locales: any[];
6
- linkToVendorTask?: undefined;
7
- } | {
8
- documentId: string;
9
- locales: any[];
10
- taskId: string;
11
- linkToVendorTask: string;
12
- }>;
@@ -1,2 +0,0 @@
1
- import { Secrets } from 'sanity-translations-tab';
2
- export declare const getLocales: (secrets: Secrets) => Promise<any>;
@@ -1,2 +0,0 @@
1
- import { Secrets } from 'sanity-translations-tab';
2
- export declare const getTranslation: (taskId: string, localeId: string, secrets: Secrets) => Promise<any>;
@@ -1,12 +0,0 @@
1
- import { Secrets } from 'sanity-translations-tab';
2
- export declare const getTranslationTask: (documentId: string, secrets: Secrets) => Promise<{
3
- documentId: string;
4
- taskId: string;
5
- locales: any[];
6
- linkToVendorTask?: undefined;
7
- } | {
8
- documentId: string;
9
- locales: any[];
10
- taskId: string;
11
- linkToVendorTask: string;
12
- }>;
@@ -1,7 +0,0 @@
1
- export declare const smartlingProxy: string;
2
- export declare const authenticate: (secret: string) => Promise<any>;
3
- export declare const getHeaders: (url: string, accessToken: string) => {
4
- Authorization: string;
5
- 'X-URL': string;
6
- };
7
- export declare const findExistingJob: (documentId: string, projectId: string, accessToken: string) => Promise<string>;
@@ -1,2 +0,0 @@
1
- import { Adapter } from 'sanity-translations-tab';
2
- export declare const SmartlingAdapter: Adapter;