From c0f55735d02b83099b08048fd44e5f59126fb812 Mon Sep 17 00:00:00 2001 From: Stephen Sawchuk Date: Mon, 18 Apr 2016 10:24:50 -0400 Subject: [PATCH] vision: support image URLs --- lib/common/grpc-service.js | 93 ++------------------------------------ lib/common/util.js | 93 ++++++++++++++++++++++++++++++++++++++ lib/vision/index.js | 55 ++++++++++++++++++++-- system-test/vision.js | 25 ++++++++++ 4 files changed, 174 insertions(+), 92 deletions(-) diff --git a/lib/common/grpc-service.js b/lib/common/grpc-service.js index 48e48b7b4e03..c2f8ad49f00a 100644 --- a/lib/common/grpc-service.js +++ b/lib/common/grpc-service.js @@ -33,95 +33,10 @@ var path = require('path'); var Service = require('./service.js'); /** - * @const {object} - A map of protobuf codes to HTTP status codes. + * @type {module:common/util} * @private */ -var HTTP_ERROR_CODE_MAP = { - 0: { - code: 200, - message: 'OK' - }, - - 1: { - code: 499, - message: 'Client Closed Request' - }, - - 2: { - code: 500, - message: 'Internal Server Error' - }, - - 3: { - code: 400, - message: 'Bad Request' - }, - - 4: { - code: 504, - message: 'Gateway Timeout' - }, - - 5: { - code: 404, - message: 'Not Found' - }, - - 6: { - code: 409, - message: 'Conflict' - }, - - 7: { - code: 403, - message: 'Forbidden' - }, - - 8: { - code: 429, - message: 'Too Many Requests' - }, - - 9: { - code: 412, - message: 'Precondition Failed' - }, - - 10: { - code: 409, - message: 'Conflict' - }, - - 11: { - code: 400, - message: 'Bad Request' - }, - - 12: { - code: 501, - message: 'Not Implemented' - }, - - 13: { - code: 500, - message: 'Internal Server Error' - }, - - 14: { - code: 503, - message: 'Service Unavailable' - }, - - 15: { - code: 500, - message: 'Internal Server Error' - }, - - 16: { - code: 401, - message: 'Unauthorized' - } -}; +var util = require('./util.js'); /** * Service is a base class, meant to be inherited from by a "service," like @@ -243,8 +158,8 @@ GrpcService.prototype.request = function(protoOpts, reqOpts, callback) { service[protoOpts.method](reqOpts, function(err, resp) { if (err) { - if (HTTP_ERROR_CODE_MAP[err.code]) { - var httpError = HTTP_ERROR_CODE_MAP[err.code]; + if (util.HTTP_ERROR_CODE_MAP[err.code]) { + var httpError = util.HTTP_ERROR_CODE_MAP[err.code]; err.code = httpError.code; } diff --git a/lib/common/util.js b/lib/common/util.js index e15f9d6e367a..39fda2651ec8 100644 --- a/lib/common/util.js +++ b/lib/common/util.js @@ -545,3 +545,96 @@ function normalizeArguments(globalContext, localConfig, options) { } util.normalizeArguments = normalizeArguments; + +/** + * @const {object} - A map of protobuf codes to HTTP status codes. + * @private + */ +var HTTP_ERROR_CODE_MAP = { + 0: { + code: 200, + message: 'OK' + }, + + 1: { + code: 499, + message: 'Client Closed Request' + }, + + 2: { + code: 500, + message: 'Internal Server Error' + }, + + 3: { + code: 400, + message: 'Bad Request' + }, + + 4: { + code: 504, + message: 'Gateway Timeout' + }, + + 5: { + code: 404, + message: 'Not Found' + }, + + 6: { + code: 409, + message: 'Conflict' + }, + + 7: { + code: 403, + message: 'Forbidden' + }, + + 8: { + code: 429, + message: 'Too Many Requests' + }, + + 9: { + code: 412, + message: 'Precondition Failed' + }, + + 10: { + code: 409, + message: 'Conflict' + }, + + 11: { + code: 400, + message: 'Bad Request' + }, + + 12: { + code: 501, + message: 'Not Implemented' + }, + + 13: { + code: 500, + message: 'Internal Server Error' + }, + + 14: { + code: 503, + message: 'Service Unavailable' + }, + + 15: { + code: 500, + message: 'Internal Server Error' + }, + + 16: { + code: 401, + message: 'Unauthorized' + } +}; + +util.HTTP_ERROR_CODE_MAP = HTTP_ERROR_CODE_MAP; diff --git a/lib/vision/index.js b/lib/vision/index.js index 1132828de324..bdbf589ae837 100644 --- a/lib/vision/index.js +++ b/lib/vision/index.js @@ -29,6 +29,7 @@ var fs = require('fs'); var is = require('is'); var nodeutil = require('util'); var prop = require('propprop'); +var request = require('request'); /** * @type {module:storage/file} @@ -315,12 +316,20 @@ Vision.prototype.detect = function(images, options, callback) { return; } + var originalResp = extend(true, {}, resp); + var errors = []; + function mergeArrayOfObjects(arr) { return extend.apply(null, arr); } function formatAnnotationBuilder(type) { return function(annotation) { + if (type === 'error') { + errors.push(Vision.formatError_(annotation)); + return []; + } + var formatMethodMap = { faceAnnotations: Vision.formatFaceAnnotation_, imagePropertiesAnnotation: Vision.formatImagePropertiesAnnotation_, @@ -335,8 +344,6 @@ Vision.prototype.detect = function(images, options, callback) { }; } - var originalResp = extend(true, {}, resp); - var detections = images .map(function() { // Group detections by image... @@ -412,8 +419,21 @@ Vision.prototype.detect = function(images, options, callback) { return annotations; }); + var respErrors = null; + if (errors.length > 0) { + respErrors = errors; + } + // If only a single image was given, expose it from the array. - callback(null, isSingleImage ? detections[0] : detections, originalResp); + if (isSingleImage) { + if (respErrors) { + respErrors = respErrors[0]; + } + + detections = detections[0]; + } + + callback(respErrors, detections, originalResp); }); }); }; @@ -1178,6 +1198,24 @@ Vision.findImages_ = function(images, callback) { return; } + // File is a URL. + if (image.indexOf('http') === 0) { + request({ + method: 'GET', + uri: image, + encoding: 'base64' + }, function(err, resp, body) { + if (err) { + callback(err); + return; + } + + callback(null, { content: body }); + }); + + return; + } + // File exists on disk. fs.readFile(image, { encoding: 'base64' }, function(err, contents) { if (err) { @@ -1234,6 +1272,17 @@ Vision.formatEntityAnnotation_ = function(entityAnnotation, options) { return formattedEntityAnnotation; }; +/** + * Format an error from the API. + * + * @private + */ +Vision.formatError_ = function(error) { + var statusError = new Error(error.message); + statusError.code = util.HTTP_ERROR_CODE_MAP[error.code].code; + return statusError; +}; + /** * Format a raw face annotation response from the API. * diff --git a/system-test/vision.js b/system-test/vision.js index ce7ddfbb47f1..73b635ef4847 100644 --- a/system-test/vision.js +++ b/system-test/vision.js @@ -17,6 +17,8 @@ 'use strict'; var assert = require('assert'); +var fs = require('fs'); +var http = require('http'); var is = require('is'); var multiline = require('multiline'); var path = require('path'); @@ -36,6 +38,29 @@ describe('Vision', function() { vision = new Vision(env); }); + it('should detect from a URL', function(done) { + var server = http.createServer(function(req, res) { + fs.readFile(IMAGES.logo, function(err, resp) { + assert.ifError(err); + res.end(resp); + }); + }); + + server.listen(8800, function(err) { + assert.ifError(err); + + var url = 'http://localhost:8800/logo.png'; + + vision.detectLogos(url, function(err, logos) { + assert.ifError(err); + + assert.deepEqual(logos, ['Google']); + + done(); + }); + }); + }); + it('should perform multiple detections', function(done) { var detectionTypes = ['faces', 'labels', 'safeSearch'];