1
+ // @ts -check
2
+
3
+ const transifex = require ( './transifex' ) ;
4
+ const fetch = require ( 'node-fetch' ) ;
5
+ const fs = require ( 'fs' ) ;
6
+ const shell = require ( 'shelljs' ) ;
7
+ const util = require ( 'util' ) ;
8
+
9
+ const uploadSourceFile = async ( organization , project , resource , filePath ) => {
10
+ const url = transifex . url ( 'resource_strings_async_uploads' ) ;
11
+ const data = {
12
+ data : {
13
+ attributes : {
14
+ callback_url : null ,
15
+ content : fs . readFileSync ( filePath ) . toString ( 'base64' ) ,
16
+ content_encoding : 'base64'
17
+ } ,
18
+ relationships : {
19
+ resource : {
20
+ data : {
21
+ id : util . format ( 'o:%s:p:%s:r:%s' , organization , project , resource ) ,
22
+ type : 'resources'
23
+ }
24
+ }
25
+ } ,
26
+ type : 'resource_strings_async_uploads'
27
+ }
28
+ } ;
29
+
30
+ const headers = transifex . authHeader ( ) ;
31
+ headers [ 'Content-Type' ] = 'application/vnd.api+json' ;
32
+ const json = await fetch ( url , { method : 'POST' , headers, body : JSON . stringify ( data ) } )
33
+ . catch ( err => {
34
+ shell . echo ( err ) ;
35
+ shell . exit ( 1 ) ;
36
+ } )
37
+ . then ( res => res . json ( ) ) ;
38
+
39
+ return json [ 'data' ] [ 'id' ] ;
40
+ } ;
41
+
42
+ const getSourceUploadStatus = async ( uploadId ) => {
43
+ const url = transifex . url ( util . format ( 'resource_strings_async_uploads/%s' , uploadId ) ) ;
44
+ // The download request status must be asked from time to time, if it's
45
+ // still pending we try again using exponentional backoff starting from 2.5 seconds.
46
+ let backoffMs = 2500 ;
47
+ const headers = transifex . authHeader ( ) ;
48
+ while ( true ) {
49
+ const json = await fetch ( url , { headers } )
50
+ . catch ( err => {
51
+ shell . echo ( err ) ;
52
+ shell . exit ( 1 ) ;
53
+ } )
54
+ . then ( res => res . json ( ) ) ;
55
+
56
+ const status = json [ 'data' ] [ 'attributes' ] [ 'status' ] ;
57
+ if ( status === 'succeeded' ) {
58
+ return
59
+ } else if ( status === 'pending' || status === 'processing' ) {
60
+ await new Promise ( r => setTimeout ( r , backoffMs ) ) ;
61
+ backoffMs = backoffMs * 2 ;
62
+ // Retry the upload request status again
63
+ continue
64
+ } else if ( status === 'failed' ) {
65
+ const errors = [ ] ;
66
+ json [ 'data' ] [ 'attributes' ] [ 'errors' ] . forEach ( err => {
67
+ errors . push ( util . format ( '%s: %s' , err . code , err . details ) ) ;
68
+ } ) ;
69
+ throw util . format ( 'Download request failed: %s' , errors . join ( ', ' ) ) ;
70
+ }
71
+ throw 'Download request failed in an unforeseen way' ;
72
+ }
73
+ }
74
+
75
+ ( async ( ) => {
76
+ const { organization, project, resource } = await transifex . credentials ( ) ;
77
+ const sourceFile = process . argv [ 2 ] ;
78
+ if ( ! sourceFile ) {
79
+ shell . echo ( 'Translation source file not specified' ) ;
80
+ shell . exit ( 1 ) ;
81
+ }
82
+
83
+ const uploadId = await uploadSourceFile ( organization , project , resource , sourceFile )
84
+ . catch ( err => {
85
+ shell . echo ( err ) ;
86
+ shell . exit ( 1 ) ;
87
+ } ) ;
88
+
89
+ await getSourceUploadStatus ( uploadId )
90
+ . catch ( err => {
91
+ shell . echo ( err ) ;
92
+ shell . exit ( 1 ) ;
93
+ } ) ;
94
+
95
+ shell . echo ( "Translation source file uploaded" ) ;
96
+ } ) ( )
0 commit comments