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