Updated imports for i18n with typings. Removed 'request' module dependency.

This commit is contained in:
Michel Kaporin
2017-03-20 15:01:18 +01:00
parent 0741a67980
commit f352caed0b
6 changed files with 202 additions and 281 deletions

View File

@@ -11,9 +11,8 @@ var File = require("vinyl");
var Is = require("is");
var xml2js = require("xml2js");
var glob = require("glob");
var http = require("http");
var util = require('gulp-util');
var request = require('request');
var es = require('event-stream');
var iconv = require('iconv-lite');
function log(message) {
var rest = [];
@@ -636,28 +635,27 @@ function importIsl(file, stream) {
stream.emit('data', xlfFile);
}
}
function pushXlfFiles(apiUrl, username, password) {
function pushXlfFiles(apiHostname, username, password) {
var tryGetPromises = [];
var updateCreatePromises = [];
return event_stream_1.through(function (file) {
var project = path.dirname(file.relative);
var fileName = path.basename(file.path);
var slug = fileName.substr(0, fileName.length - '.xlf'.length);
var credentials = {
'user': username,
'password': password
};
var credentials = username + ":" + password;
// Check if resource already exists, if not, then create it.
var promise = tryGetResource(project, slug, apiUrl, credentials);
var promise = tryGetResource(project, slug, apiHostname, credentials);
tryGetPromises.push(promise);
promise.then(function (exists) {
if (exists) {
promise = updateResource(project, slug, file, apiUrl, credentials);
promise = updateResource(project, slug, file, apiHostname, credentials);
}
else {
promise = createResource(project, slug, file, apiUrl, credentials);
promise = createResource(project, slug, file, apiHostname, credentials);
}
updateCreatePromises.push(promise);
}).catch(function (reason) {
log('Error:', reason);
});
}, function () {
var _this = this;
@@ -665,15 +663,20 @@ function pushXlfFiles(apiUrl, username, password) {
Promise.all(tryGetPromises).then(function () {
Promise.all(updateCreatePromises).then(function () {
_this.emit('end');
});
});
}).catch(function (reason) { return log('Error:', reason); });
}).catch(function (reason) { return log('Error:', reason); });
});
}
exports.pushXlfFiles = pushXlfFiles;
function tryGetResource(project, slug, apiUrl, credentials) {
function tryGetResource(project, slug, apiHostname, credentials) {
return new Promise(function (resolve, reject) {
var url = apiUrl + "/project/" + project + "/resource/" + slug + "/?details";
request.get(url, { 'auth': credentials }).on('response', function (response) {
var options = {
hostname: apiHostname,
path: "/api/2/project/" + project + "/resource/" + slug + "/?details",
auth: credentials,
method: 'GET'
};
var request = http.request(options, function (response) {
if (response.statusCode === 404) {
resolve(false);
}
@@ -681,62 +684,84 @@ function tryGetResource(project, slug, apiUrl, credentials) {
resolve(true);
}
else {
reject("Failed to query resource " + slug + ". Response: " + response.statusCode + " " + response.statusMessage);
reject("Failed to query resource " + project + "/" + slug + ". Response: " + response.statusCode + " " + response.statusMessage);
}
}).on('error', function (err) {
reject("Failed to get " + project + "/" + slug + " on Transifex: " + err);
});
request.end();
});
}
function createResource(project, slug, xlfFile, apiUrl, credentials) {
return new Promise(function (resolve) {
var url = apiUrl + "/project/" + project + "/resources";
function createResource(project, slug, xlfFile, apiHostname, credentials) {
return new Promise(function (resolve, reject) {
var data = JSON.stringify({
'content': xlfFile.contents.toString(),
'name': slug,
'slug': slug,
'i18n_type': 'XLIFF'
});
var options = {
'body': {
'content': xlfFile.contents.toString(),
'name': slug,
'slug': slug,
'i18n_type': 'XLIFF'
hostname: apiHostname,
path: "/api/2/project/" + project + "/resources",
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(data)
},
'json': true,
'auth': credentials
auth: credentials,
method: 'POST'
};
request.post(url, options, function (err, res) {
if (err) {
log('Error:', "Failed to create Transifex " + project + "/" + slug + ": " + err);
}
var request = http.request(options, function (res) {
if (res.statusCode === 201) {
log("Resource " + project + "/" + slug + " successfully created on Transifex.");
resolve();
}
else {
log('Error:', "Something went wrong creating " + slug + " in " + project + ". " + res.statusCode);
reject("Something went wrong in the request creating " + slug + " in " + project + ". " + res.statusCode);
}
}).on('error', function (err) {
reject("Failed to create " + project + "/" + slug + " on Transifex: " + err);
});
request.write(data);
request.end();
});
}
/**
* The following link provides information about how Transifex handles updates of a resource file:
* https://dev.befoolish.co/tx-docs/public/projects/updating-content#what-happens-when-you-update-files
*/
function updateResource(project, slug, xlfFile, apiUrl, credentials) {
return new Promise(function (resolve) {
var url = apiUrl + "/project/" + project + "/resource/" + slug + "/content";
function updateResource(project, slug, xlfFile, apiHostname, credentials) {
return new Promise(function (resolve, reject) {
var data = JSON.stringify({ content: xlfFile.contents.toString() });
var options = {
'body': { 'content': xlfFile.contents.toString() },
'json': true,
'auth': credentials
hostname: apiHostname,
path: "/api/2/project/" + project + "/resource/" + slug + "/content",
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(data)
},
auth: credentials,
method: 'PUT'
};
request.put(url, options, function (err, res, body) {
if (err) {
log('Error:', "Failed to update Transifex " + project + "/" + slug + ": " + err);
}
var request = http.request(options, function (res) {
if (res.statusCode === 200) {
log("Resource " + project + "/" + slug + " successfully updated on Transifex. Strings added: " + body['strings_added'] + ", updated: " + body['strings_updated'] + ", deleted: " + body['strings_delete']);
resolve();
res.setEncoding('utf8');
var responseBuffer_1 = '';
res.on('data', function (chunk) {
responseBuffer_1 += chunk;
});
res.on('end', function () {
var response = JSON.parse(responseBuffer_1);
log("Resource " + project + "/" + slug + " successfully updated on Transifex. Strings added: " + response.strings_added + ", updated: " + response.strings_added + ", deleted: " + response.strings_added);
resolve();
});
}
else {
log('Error:', "Something went wrong updating " + slug + " in " + project + ". " + res.statusCode);
reject("Something went wrong in the request updating " + slug + " in " + project + ". " + res.statusCode);
}
}).on('error', function (err) {
reject("Failed to update " + project + "/" + slug + " on Transifex: " + err);
});
request.write(data);
request.end();
});
}
function getMetadataResources(pathToMetadata) {
@@ -773,22 +798,17 @@ function obtainProjectResources(projectName) {
}
return resources;
}
function pullXlfFiles(projectName, apiUrl, username, password, resources) {
function pullXlfFiles(projectName, apiHostname, username, password, resources) {
if (!resources) {
resources = obtainProjectResources(projectName);
}
if (!resources) {
throw new Error('Transifex projects and resources must be defined to be able to pull translations from Transifex.');
}
var credentials = {
'auth': {
'user': username,
'password': password
}
};
var credentials = username + ":" + password;
var expectedTranslationsCount = vscodeLanguages.length * resources.length;
var translationsRetrieved = 0, called = false;
return es.readable(function (count, callback) {
return event_stream_1.readable(function (count, callback) {
// Mark end of stream when all resources were retrieved
if (translationsRetrieved === expectedTranslationsCount) {
return this.emit('end');
@@ -801,25 +821,28 @@ function pullXlfFiles(projectName, apiUrl, username, password, resources) {
var slug = resource.name.replace(/\//g, '_');
var project = resource.project;
var iso639 = iso639_3_to_2[language];
var url = apiUrl + "/project/" + project + "/resource/" + slug + "/translation/" + iso639 + "?file&mode=onlyreviewed";
var xlfBuffer = '', responseCode;
request.get(url, credentials)
.on('response', function (response) {
responseCode = response.statusCode;
})
.on('data', function (data) { return xlfBuffer += data; })
.on('end', function () {
if (responseCode === 200) {
stream_1.emit('data', new File({ contents: new Buffer(xlfBuffer) }));
}
else {
log('Error:', slug + " in " + project + " returned no data. Response code: " + responseCode + ".");
}
translationsRetrieved++;
})
.on('error', function (error) {
log('Error:', "Failed to query resource " + slug + " with the following error: " + error);
var options = {
hostname: apiHostname,
path: "/api/2/project/" + project + "/resource/" + slug + "/translation/" + iso639 + "?file&mode=onlyreviewed",
auth: credentials,
method: 'GET'
};
var request = http.request(options, function (res) {
var xlfBuffer = '';
res.on('data', function (data) { return xlfBuffer += data; });
res.on('end', function () {
if (res.statusCode === 200) {
stream_1.emit('data', new File({ contents: new Buffer(xlfBuffer) }));
}
else {
log('Error:', slug + " in " + project + " returned no data. Response code: " + res.statusCode + ".");
}
translationsRetrieved++;
});
}).on('error', function (err) {
log('Error:', "Failed to query resource " + slug + " with the following error: " + err);
});
request.end();
});
});
}

View File

@@ -6,16 +6,15 @@
import * as path from 'path';
import * as fs from 'fs';
import { through } from 'event-stream';
import { through, readable } from 'event-stream';
import { ThroughStream } from 'through';
import File = require('vinyl');
import * as Is from 'is';
import xml2js = require('xml2js');
import * as xml2js from 'xml2js';
import * as glob from 'glob';
import * as http from 'http';
var util = require('gulp-util');
const request = require('request');
const es = require('event-stream');
var iconv = require('iconv-lite');
function log(message: any, ...rest: any[]): void {
@@ -719,7 +718,7 @@ function importIsl(file: File, stream: ThroughStream) {
}
}
export function pushXlfFiles(apiUrl: string, username: string, password: string): ThroughStream {
export function pushXlfFiles(apiHostname: string, username: string, password: string): ThroughStream {
let tryGetPromises = [];
let updateCreatePromises = [];
@@ -727,21 +726,20 @@ export function pushXlfFiles(apiUrl: string, username: string, password: string)
const project = path.dirname(file.relative);
const fileName = path.basename(file.path);
const slug = fileName.substr(0, fileName.length - '.xlf'.length);
const credentials = {
'user': username,
'password': password
};
const credentials = `${username}:${password}`;
// Check if resource already exists, if not, then create it.
let promise = tryGetResource(project, slug, apiUrl, credentials);
let promise = tryGetResource(project, slug, apiHostname, credentials);
tryGetPromises.push(promise);
promise.then(exists => {
if (exists) {
promise = updateResource(project, slug, file, apiUrl, credentials);
promise = updateResource(project, slug, file, apiHostname, credentials);
} else {
promise = createResource(project, slug, file, apiUrl, credentials);
promise = createResource(project, slug, file, apiHostname, credentials);
}
updateCreatePromises.push(promise);
}).catch((reason) => {
log('Error:', reason);
});
}, function() {
@@ -749,52 +747,67 @@ export function pushXlfFiles(apiUrl: string, username: string, password: string)
Promise.all(tryGetPromises).then(() => {
Promise.all(updateCreatePromises).then(() => {
this.emit('end');
});
});
}).catch((reason) => log('Error:', reason));
}).catch((reason) => log('Error:', reason));
});
}
function tryGetResource(project: string, slug: string, apiUrl: string, credentials: any): Promise<boolean> {
function tryGetResource(project: string, slug: string, apiHostname: string, credentials: string): Promise<boolean> {
return new Promise((resolve, reject) => {
const url = `${apiUrl}/project/${project}/resource/${slug}/?details`;
request.get(url, { 'auth': credentials }).on('response', function (response) {
const options = {
hostname: apiHostname,
path: `/api/2/project/${project}/resource/${slug}/?details`,
auth: credentials,
method: 'GET'
};
const request = http.request(options, (response) => {
if (response.statusCode === 404) {
resolve(false);
} else if (response.statusCode === 200) {
resolve(true);
} else {
reject(`Failed to query resource ${slug}. Response: ${response.statusCode} ${response.statusMessage}`);
reject(`Failed to query resource ${project}/${slug}. Response: ${response.statusCode} ${response.statusMessage}`);
}
}).on('error', (err) => {
reject(`Failed to get ${project}/${slug} on Transifex: ${err}`);
});
request.end();
});
}
function createResource(project: string, slug: string, xlfFile: File, apiUrl:string, credentials: any): Promise<any> {
return new Promise((resolve) => {
const url = `${apiUrl}/project/${project}/resources`;
function createResource(project: string, slug: string, xlfFile: File, apiHostname: string, credentials: any): Promise<any> {
return new Promise((resolve, reject) => {
const data = JSON.stringify({
'content': xlfFile.contents.toString(),
'name': slug,
'slug': slug,
'i18n_type': 'XLIFF'
});
const options = {
'body': {
'content': xlfFile.contents.toString(),
'name': slug,
'slug': slug,
'i18n_type': 'XLIFF'
hostname: apiHostname,
path: `/api/2/project/${project}/resources`,
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(data)
},
'json': true,
'auth': credentials
auth: credentials,
method: 'POST'
};
request.post(url, options, function(err, res) {
if (err) {
log('Error:', `Failed to create Transifex ${project}/${slug}: ${err}`);
}
let request = http.request(options, (res) => {
if (res.statusCode === 201) {
log(`Resource ${project}/${slug} successfully created on Transifex.`);
resolve();
} else {
log('Error:', `Something went wrong creating ${slug} in ${project}. ${res.statusCode}`);
reject(`Something went wrong in the request creating ${slug} in ${project}. ${res.statusCode}`);
}
}).on('error', (err) => {
reject(`Failed to create ${project}/${slug} on Transifex: ${err}`);
});
request.write(data);
request.end();
});
}
@@ -802,27 +815,42 @@ function createResource(project: string, slug: string, xlfFile: File, apiUrl:str
* The following link provides information about how Transifex handles updates of a resource file:
* https://dev.befoolish.co/tx-docs/public/projects/updating-content#what-happens-when-you-update-files
*/
function updateResource(project: string, slug: string, xlfFile: File, apiUrl: string, credentials: any) : Promise<any> {
return new Promise((resolve) => {
const url = `${apiUrl}/project/${project}/resource/${slug}/content`;
function updateResource(project: string, slug: string, xlfFile: File, apiHostname: string, credentials: string) : Promise<any> {
return new Promise((resolve, reject) => {
const data = JSON.stringify({ content: xlfFile.contents.toString() });
const options = {
'body': { 'content': xlfFile.contents.toString() },
'json': true,
'auth': credentials
hostname: apiHostname,
path: `/api/2/project/${project}/resource/${slug}/content`,
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(data)
},
auth: credentials,
method: 'PUT'
};
request.put(url, options, function(err, res, body) {
if (err) {
log('Error:', `Failed to update Transifex ${project}/${slug}: ${err}`);
}
let request = http.request(options, (res) => {
if (res.statusCode === 200) {
log(`Resource ${project}/${slug} successfully updated on Transifex. Strings added: ${body['strings_added']}, updated: ${body['strings_updated']}, deleted: ${body['strings_delete']}`);
resolve();
res.setEncoding('utf8');
let responseBuffer: string = '';
res.on('data', function (chunk) {
responseBuffer += chunk;
});
res.on('end', () => {
const response = JSON.parse(responseBuffer);
log(`Resource ${project}/${slug} successfully updated on Transifex. Strings added: ${response.strings_added}, updated: ${response.strings_added}, deleted: ${response.strings_added}`);
resolve();
});
} else {
log('Error:', `Something went wrong updating ${slug} in ${project}. ${res.statusCode}`);
reject(`Something went wrong in the request updating ${slug} in ${project}. ${res.statusCode}`);
}
}).on('error', (err) => {
reject(`Failed to update ${project}/${slug} on Transifex: ${err}`);
});
request.write(data);
request.end();
});
}
@@ -863,7 +891,7 @@ function obtainProjectResources(projectName: string): Resource[] {
return resources;
}
export function pullXlfFiles(projectName: string, apiUrl: string, username: string, password: string, resources?: Resource[]): NodeJS.ReadableStream {
export function pullXlfFiles(projectName: string, apiHostname: string, username: string, password: string, resources?: Resource[]): NodeJS.ReadableStream {
if (!resources) {
resources = obtainProjectResources(projectName);
}
@@ -871,16 +899,11 @@ export function pullXlfFiles(projectName: string, apiUrl: string, username: stri
throw new Error('Transifex projects and resources must be defined to be able to pull translations from Transifex.');
}
const credentials = {
'auth': {
'user': username,
'password': password
}
};
const credentials = `${username}:${password}`;
let expectedTranslationsCount = vscodeLanguages.length * resources.length;
let translationsRetrieved = 0, called = false;
return es.readable(function(count, callback) {
return readable(function(count, callback) {
// Mark end of stream when all resources were retrieved
if (translationsRetrieved === expectedTranslationsCount) {
return this.emit('end');
@@ -895,25 +918,28 @@ export function pullXlfFiles(projectName: string, apiUrl: string, username: stri
const slug = resource.name.replace(/\//g, '_');
const project = resource.project;
const iso639 = iso639_3_to_2[language];
const url = `${apiUrl}/project/${project}/resource/${slug}/translation/${iso639}?file&mode=onlyreviewed`;
const options = {
hostname: apiHostname,
path: `/api/2/project/${project}/resource/${slug}/translation/${iso639}?file&mode=onlyreviewed`,
auth: credentials,
method: 'GET'
};
let xlfBuffer: string = '', responseCode: number;
request.get(url, credentials)
.on('response', (response) => {
responseCode = response.statusCode;
})
.on('data', (data) => xlfBuffer += data)
.on('end', () => {
if (responseCode === 200) {
stream.emit('data', new File({ contents: new Buffer(xlfBuffer) }));
} else {
log('Error:', `${slug} in ${project} returned no data. Response code: ${responseCode}.`);
}
translationsRetrieved++;
})
.on('error', (error) => {
log('Error:', `Failed to query resource ${slug} with the following error: ${error}`);
});
let request = http.request(options, (res) => {
let xlfBuffer: string = '';
res.on('data', (data) => xlfBuffer += data);
res.on('end', () => {
if (res.statusCode === 200) {
stream.emit('data', new File({ contents: new Buffer(xlfBuffer) }));
} else {
log('Error:', `${slug} in ${project} returned no data. Response code: ${res.statusCode}.`);
}
translationsRetrieved++;
});
}).on('error', (err) => {
log('Error:', `Failed to query resource ${slug} with the following error: ${err}`);
});
request.end();
});
});
}

View File

@@ -1,6 +1,7 @@
declare module "event-stream" {
import { Stream } from 'stream';
import { ThroughStream } from 'through';
import { MapStream } from 'map-stream';
function merge(streams: Stream[]): ThroughStream;
function merge(...streams: Stream[]): ThroughStream;
@@ -16,4 +17,5 @@ declare module "event-stream" {
function mapSync<I,O>(cb: (data:I) => O): ThroughStream;
function map<I,O>(cb: (data:I, cb:(err?:Error, data?: O)=>void) => O): ThroughStream;
function readable(asyncFunction: Function): MapStream;
}

View File

@@ -1,22 +0,0 @@
// Type definitions for through
// Project: https://github.com/dominictarr/through
// Definitions by: Andrew Gaspar <https://github.com/AndrewGaspar/>
// Definitions: https://github.com/borisyankov/DefinitelyTyped
declare module "through" {
import stream = require("stream");
function through(write?: (data:any) => void,
end?: () => void,
opts?: {
autoDestroy: boolean;
}): through.ThroughStream;
module through {
export interface ThroughStream extends stream.Transform {
autoDestroy: boolean;
}
}
export = through;
}

View File

@@ -1,112 +0,0 @@
// Type definitions for vinyl 0.4.3
// Project: https://github.com/wearefractal/vinyl
// Definitions by: vvakame <https://github.com/vvakame/>, jedmao <https://github.com/jedmao>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
declare module "vinyl" {
import fs = require("fs");
/**
* A virtual file format.
*/
class File {
constructor(options?: {
/**
* Default: process.cwd()
*/
cwd?: string;
/**
* Used for relative pathing. Typically where a glob starts.
*/
base?: string;
/**
* Full path to the file.
*/
path?: string;
/**
* Path history. Has no effect if options.path is passed.
*/
history?: string[];
/**
* The result of an fs.stat call. See fs.Stats for more information.
*/
stat?: fs.Stats;
/**
* File contents.
* Type: Buffer, Stream, or null
*/
contents?: Buffer | NodeJS.ReadWriteStream;
});
/**
* Default: process.cwd()
*/
public cwd: string;
/**
* Used for relative pathing. Typically where a glob starts.
*/
public base: string;
/**
* Full path to the file.
*/
public path: string;
public stat: fs.Stats;
/**
* Type: Buffer|Stream|null (Default: null)
*/
public contents: Buffer | NodeJS.ReadableStream;
/**
* Returns path.relative for the file base and file path.
* Example:
* var file = new File({
* cwd: "/",
* base: "/test/",
* path: "/test/file.js"
* });
* console.log(file.relative); // file.js
*/
public relative: string;
public isBuffer(): boolean;
public isStream(): boolean;
public isNull(): boolean;
public isDirectory(): boolean;
/**
* Returns a new File object with all attributes cloned. Custom attributes are deep-cloned.
*/
public clone(opts?: { contents?: boolean }): File;
/**
* If file.contents is a Buffer, it will write it to the stream.
* If file.contents is a Stream, it will pipe it to the stream.
* If file.contents is null, it will do nothing.
*/
public pipe<T extends NodeJS.ReadWriteStream>(
stream: T,
opts?: {
/**
* If false, the destination stream will not be ended (same as node core).
*/
end?: boolean;
}): T;
/**
* Returns a pretty String interpretation of the File. Useful for console.log.
*/
public inspect(): string;
}
/**
* This is required as per:
* https://github.com/Microsoft/TypeScript/issues/5073
*/
namespace File {}
export = File;
}

View File

@@ -46,7 +46,10 @@
"@types/mocha": "^2.2.39",
"@types/semver": "^5.3.30",
"@types/sinon": "^1.16.34",
"@types/through": "^0.0.28",
"@types/winreg": "^1.2.30",
"@types/vinyl": "^2.0.0",
"@types/xml2js": "^0.0.33",
"azure-storage": "^0.3.1",
"clean-css": "3.4.6",
"coveralls": "^2.11.11",
@@ -105,7 +108,8 @@
"underscore": "^1.8.2",
"vinyl": "^0.4.5",
"vinyl-fs": "^2.4.3",
"vscode-nls-dev": "^2.0.1"
"vscode-nls-dev": "^2.0.1",
"xml2js": "^0.4.17"
},
"repository": {
"type": "git",