From f352caed0b2e3018a44bfd2a1a67a6a0f160ccd4 Mon Sep 17 00:00:00 2001 From: Michel Kaporin Date: Mon, 20 Mar 2017 15:01:18 +0100 Subject: [PATCH] Updated imports for i18n with typings. Removed 'request' module dependency. --- build/lib/i18n.js | 165 +++++++++++++++----------- build/lib/i18n.ts | 176 ++++++++++++++++------------ build/lib/typings/event-stream.d.ts | 2 + build/lib/typings/through.d.ts | 22 ---- build/lib/typings/vinyl.d.ts | 112 ------------------ package.json | 6 +- 6 files changed, 202 insertions(+), 281 deletions(-) delete mode 100644 build/lib/typings/through.d.ts delete mode 100644 build/lib/typings/vinyl.d.ts diff --git a/build/lib/i18n.js b/build/lib/i18n.js index 7176c3af702..896fc9e3a55 100644 --- a/build/lib/i18n.js +++ b/build/lib/i18n.js @@ -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(); }); }); } diff --git a/build/lib/i18n.ts b/build/lib/i18n.ts index 10708e23705..24971e98500 100644 --- a/build/lib/i18n.ts +++ b/build/lib/i18n.ts @@ -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 { +function tryGetResource(project: string, slug: string, apiHostname: string, credentials: string): Promise { 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 { - return new Promise((resolve) => { - const url = `${apiUrl}/project/${project}/resources`; +function createResource(project: string, slug: string, xlfFile: File, apiHostname: string, credentials: any): Promise { + 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 { - 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 { + 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(); }); }); } diff --git a/build/lib/typings/event-stream.d.ts b/build/lib/typings/event-stream.d.ts index 2867c3eff36..7e5ccee5e17 100644 --- a/build/lib/typings/event-stream.d.ts +++ b/build/lib/typings/event-stream.d.ts @@ -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(cb: (data:I) => O): ThroughStream; function map(cb: (data:I, cb:(err?:Error, data?: O)=>void) => O): ThroughStream; + function readable(asyncFunction: Function): MapStream; } \ No newline at end of file diff --git a/build/lib/typings/through.d.ts b/build/lib/typings/through.d.ts deleted file mode 100644 index 254b844fb23..00000000000 --- a/build/lib/typings/through.d.ts +++ /dev/null @@ -1,22 +0,0 @@ -// Type definitions for through -// Project: https://github.com/dominictarr/through -// Definitions by: Andrew Gaspar -// 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; -} \ No newline at end of file diff --git a/build/lib/typings/vinyl.d.ts b/build/lib/typings/vinyl.d.ts deleted file mode 100644 index a85632e172b..00000000000 --- a/build/lib/typings/vinyl.d.ts +++ /dev/null @@ -1,112 +0,0 @@ -// Type definitions for vinyl 0.4.3 -// Project: https://github.com/wearefractal/vinyl -// Definitions by: vvakame , 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( - 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; - -} \ No newline at end of file diff --git a/package.json b/package.json index a2eb66d12f7..fd917b149a1 100644 --- a/package.json +++ b/package.json @@ -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",