Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
cdanielw committed Mar 5, 2021
2 parents 0b5133b + 114dcad commit 727d1d1
Show file tree
Hide file tree
Showing 63 changed files with 5,800 additions and 4,016 deletions.
4 changes: 0 additions & 4 deletions demos/wmts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ Layers are taken from a central Google Cloud Storage bucket

To run this app, first create an App Engine application as per the Developer
Docs instructions for [deploying an EE-based App Engine app][1].
Note that the service account created in the "Set up credentials" step needs to
be registered with Earth Engine; one can only do this currently by emailing
`[email protected]` for it to be added to the whitelist; in the future (Q1
2020), this validation process will be more streamlined.

## Deploying

Expand Down
14 changes: 6 additions & 8 deletions demos/wmts/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,18 @@
# Lint as: python3
"""Handle the routing for the application."""


import cachetools
import collections
import json
import os
import threading
from xml.etree import ElementTree

from absl import logging
import cachetools
import cloudstorage as gcs
import flask
from flask import request
from flask_wtf import csrf
import httplib
import requests

from google.appengine.api import app_identity
Expand Down Expand Up @@ -49,8 +47,8 @@ def setup_ee():
"""Sets up Earth Engine authentication."""
with open("privatekey.json") as keyfile:
extracted_key_data = keyfile.read()
credentials = ee.ServiceAccountCredentials(config.EE_SERVICE_ACCOUNT,
key_data=extracted_key_data)
credentials = ee.ServiceAccountCredentials(
config.EE_SERVICE_ACCOUNT, key_data=extracted_key_data)
ee.Initialize(credentials)


Expand All @@ -64,14 +62,14 @@ def restrict_frames(response):
def csrf_error():
return flask.make_response(
flask.jsonify(error="CSRF token is expired; please refresh the page."),
httplib.BAD_REQUEST)
400)


@server.errorhandler(httplib.BAD_REQUEST)
@server.errorhandler(400)
def bad_request(error):
return flask.make_response(
flask.jsonify(error=error.description),
httplib.BAD_REQUEST)
400)


def _getint(d, key, default=None):
Expand Down
1,269 changes: 624 additions & 645 deletions javascript/build/ee_api_js.js

Large diffs are not rendered by default.

1,997 changes: 1,024 additions & 973 deletions javascript/build/ee_api_js_debug.js

Large diffs are not rendered by default.

2,033 changes: 1,044 additions & 989 deletions javascript/build/ee_api_js_npm.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion javascript/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@google/earthengine",
"version": "0.1.242",
"version": "0.1.254",
"description": "JavaScript client for Google Earth Engine API.",
"author": "Google LLC",
"license": "Apache-2.0",
Expand Down
86 changes: 25 additions & 61 deletions javascript/src/apiclient.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ const {PromiseRequestService} = goog.require('eeapiclient.promise_request_servic
const apiclient = {};


const API_CLIENT_VERSION = '0.1.242';
const LEGACY_DOWNLOAD_REGEX = /^\/(table).*/;
const API_CLIENT_VERSION = '0.1.254';

exports.VERSION = apiVersion.VERSION;
exports.API_CLIENT_VERSION = API_CLIENT_VERSION;
Expand Down Expand Up @@ -77,9 +76,16 @@ class Call {
if (response instanceof Promise) {
// Async mode: invoke the callback with the result of the promise.
if (this.callback) {
response.then((result) => this.callback(result)).catch(
// Any errors from the then clause will also be passed to the catch.
(error) => this.callback(undefined, /** @type {string} */(error)));
const callback = (result, error=undefined) => {
try {
return this.callback(result, /** @type {string} */ (error));
} catch (callbackError) {
setTimeout(() => { // Do the throw outside the Promise handlers.
throw callbackError;
}, 0);
}
};
response.then(callback, (error) => callback(undefined, error));
}
// We should not be running in async mode without a callback, but it is
// still useful to return the promise.
Expand Down Expand Up @@ -402,21 +408,6 @@ apiclient.getProject = function() {
return apiclient.project_;
};

/**
* Enables use of the Cloud API when making requests.
* @param {boolean} enable
*/
apiclient.setCloudApiEnabled = function(enable) {
apiclient.cloudApiEnabled_ = enable;
};

/**
* @return {boolean} True if the Cloud API is enabled.
*/
apiclient.getCloudApiEnabled = function() {
return apiclient.cloudApiEnabled_;
};


/**
* Constructs a URL for API requests that is safe to use in both browser and
Expand Down Expand Up @@ -452,10 +443,7 @@ apiclient.getSafeApiUrl = function() {
apiclient.setAuthToken = function(
clientId, tokenType, accessToken, expiresIn, extraScopes, callback,
updateAuthLibrary) {
const scopes = [apiclient.AUTH_SCOPE_];
if (apiclient.cloudApiEnabled_) {
scopes.push(apiclient.CLOUD_PLATFORM_SCOPE_);
}
const scopes = [apiclient.AUTH_SCOPE_, apiclient.CLOUD_PLATFORM_SCOPE_];
if (extraScopes) {
array.extend(scopes, extraScopes);
array.removeDuplicates(scopes);
Expand Down Expand Up @@ -514,7 +502,7 @@ apiclient.refreshAuthToken = function(success, error, onImmediateFailed) {
if (result.error == 'immediate_failed' && onImmediateFailed) {
onImmediateFailed();
} else {
if (apiclient.cloudApiEnabled_ && 'window' in goog.global) {
if ('window' in goog.global) {
try {
// Refresh the library auth token and handle error propagation.
apiclient.ensureAuthLibLoaded_(function() {
Expand Down Expand Up @@ -560,7 +548,7 @@ apiclient.setAuthTokenRefresher = function(refresher) {
*/
apiclient.getAuthToken = function() {
const isExpired = apiclient.authTokenExpiration_ &&
(goog.now() - apiclient.authTokenExpiration_) >= 0;
(Date.now() - apiclient.authTokenExpiration_) >= 0;
if (isExpired) {
apiclient.clearAuthToken();
}
Expand Down Expand Up @@ -650,9 +638,7 @@ apiclient.initialize = function(apiBaseUrl, tileBaseUrl, xsrfToken) {
if (xsrfToken !== undefined) { // Passing an explicit null clears it.
apiclient.xsrfToken_ = xsrfToken;
}
if (apiclient.cloudApiEnabled_) {
apiclient.setProject(apiclient.getProject() || apiclient.DEFAULT_PROJECT_);
}
apiclient.setProject(apiclient.getProject() || apiclient.DEFAULT_PROJECT_);
apiclient.initialized_ = true;
};

Expand Down Expand Up @@ -773,9 +759,7 @@ apiclient.send = function(
const headers = {
'Content-Type': contentType,
};
const forceLegacyApi = LEGACY_DOWNLOAD_REGEX.test(path);
if (API_CLIENT_VERSION && apiclient.getCloudApiEnabled() &&
!forceLegacyApi) {
if (API_CLIENT_VERSION) {
let version = API_CLIENT_VERSION;
headers[apiclient.API_CLIENT_VERSION_HEADER] = 'ee-js/' + version;
}
Expand All @@ -801,18 +785,12 @@ apiclient.send = function(
params.add('key', apiclient.cloudApiKey_);
}

if (apiclient.cloudApiEnabled_) {
if (profileHookAtCallTime) {
headers[apiclient.PROFILE_REQUEST_HEADER] = '1';
}
if (apiclient.getProject() &&
apiclient.getProject() !== apiclient.DEFAULT_PROJECT_ &&
!forceLegacyApi) {
headers[apiclient.USER_PROJECT_OVERRIDE_HEADER_] = apiclient.getProject();
}
} else
if (profileHookAtCallTime) {
params.add('profiling', '1'); // Request profiling results.
headers[apiclient.PROFILE_REQUEST_HEADER] = '1';
}
if (apiclient.getProject() &&
apiclient.getProject() !== apiclient.DEFAULT_PROJECT_) {
headers[apiclient.USER_PROJECT_OVERRIDE_HEADER_] = apiclient.getProject();
}

params = apiclient.paramAugmenter_(params, path);
Expand Down Expand Up @@ -989,12 +967,6 @@ apiclient.handleResponse_ = function(
if (profileId && profileHook) {
profileHook(profileId);
}
const getData = (response) => {
if (apiclient.cloudApiEnabled_) {
return response;
}
return response['data'];
};
const parseJson = (body) => {
try {
const response = JSON.parse(body);
Expand Down Expand Up @@ -1024,7 +996,7 @@ apiclient.handleResponse_ = function(
if (contentType === 'application/json' || contentType === 'text/json') {
const response = parseJson(responseText);
if (response.parsed) {
data = getData(response.parsed);
data = response.parsed;
if (data === undefined) {
errorMessage = 'Malformed response: ' + responseText;
}
Expand All @@ -1037,7 +1009,7 @@ apiclient.handleResponse_ = function(
apiclient.parseBatchReply(typeHeader, responseText, (id, status, text) => {
const response = parseJson(text);
if (response.parsed) {
data[id] = getData(response.parsed);
data[id] = response.parsed;
}
const error = (response.parsed ? '' : response) || statusError(status);
if (error) {
Expand Down Expand Up @@ -1085,7 +1057,7 @@ apiclient.ensureAuthLibLoaded_ = function(callback) {
done();
} else {
// The library is not loaded; load it now.
let callbackName = goog.now().toString(36);
let callbackName = Date.now().toString(36);
while (callbackName in goog.global) {
callbackName += '_';
}
Expand Down Expand Up @@ -1138,7 +1110,7 @@ apiclient.handleAuthResult_ = function(success, error, result) {
timeout['unref']();
}

apiclient.authTokenExpiration_ = goog.now() + expiresInMs;
apiclient.authTokenExpiration_ = Date.now() + expiresInMs;
}
apiclient.authToken_ = token;
if (success) {
Expand Down Expand Up @@ -1503,12 +1475,6 @@ apiclient.STORAGE_SCOPE_ =
*/
apiclient.cloudApiKey_ = null;

/**
* Enables the Cloud API library.
* @private {boolean}
*/
apiclient.cloudApiEnabled_ = true;


/**
* Whether the library has been initialized.
Expand Down Expand Up @@ -1668,8 +1634,6 @@ exports.setApiKey = apiclient.setApiKey;
exports.getApiKey = apiclient.getApiKey;
exports.setProject = apiclient.setProject;
exports.getProject = apiclient.getProject;
exports.setCloudApiEnabled = apiclient.setCloudApiEnabled;
exports.getCloudApiEnabled = apiclient.getCloudApiEnabled;
exports.DEFAULT_PROJECT = apiclient.DEFAULT_PROJECT_;
exports.PROFILE_HEADER = apiclient.PROFILE_HEADER;
exports.PROFILE_REQUEST_HEADER = apiclient.PROFILE_REQUEST_HEADER;
Expand Down
94 changes: 92 additions & 2 deletions javascript/src/arguments.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,96 @@ goog.require('goog.object');
goog.require('goog.string');
goog.require('goog.structs.Set');

/**
* Extracts arguments for the passed-in function (has to be defined with the
* 'function' keyword) from originalArgs, which could either be an array of
* many sequenced arguments OR contain a single argument dictionary. See
* extractFromClassConstructor() and extractFromClassMethod() for other cases.
*
* This helper allows users to call JS functions with argument
* dictionaries, for example:
*
* ee.data.getAssetAcl({assetId: 'users/foo/bar'});
*
* EXAMPLE USAGE:
*
* ee.exampleFn = function(param1, opt_param2) {
* args = ee.arguments.extractFromFunction(ee.exampleFn, arguments);
* doSomethingWith(args['param1'], args['param2']);
* }
*
* Note: args only contains parameters that are provided.
*
* The following discussions also apply to extractFromClassConstructor() and
* extractFromClassMethod().
*
* USAGE WARNING:
*
* This helper is safe to use only in functions that meet at least one
* of the following conditions:
*
* A) The function requires two or more arguments.
* B) The function takes only an ee.ComputedObject as its first argument.
* C) The function does not accept a "normal" object (i.e. a JavaScript
* Object that's not an Array or Function) as its first argument.
* D) The function accepts a "normal" object as its first argument, and
* i) this object can only contain a limited set of possible keys
* AND ii) the intersection between possible keys and the function's
* expected params names is empty.
* A visParam object is an example.
*
* This helper may not work with complex default parameter values that include
* parentheses or commas:
*
* function(param1, param2 = {name: 'Chewbacca', age: 200}) {...}
* or function(param1, param2 = function() {...}) {...}
*
* USAGE WARNING EXPLANATION:
*
* If these constraints are unmet, this helper cannot always tell whether
* the user intended to:
*
* 1) Pass a dictionary of keyed arguments
* e.g. ee.Dictionary({dict: {myKey: 'myValue'}});
* or 2) Just pass a single object as a normal argument in sequence
* e.g. ee.Dictionary({myKey: 'myValue'});
*
* To try to differentiate the two, we test whether all of the keys in the
* first argument match the expected parameter names. This is guaranteed to
* work for case (D) above. In other cases, there MAY be mistakes,
* for example if the user wanted "myKey" above to have the value "dict",
* we might mistakenly think the user intended #1 when in fact they wanted #2.
*
* Another case is when a user passes in an empty object {} to represent that
* no arguments are provided, we would mistakenly treat the empty object {} as
* the first argument. So in such cases, just do not pass any thing in the
* function call.
*
* COMPILATION WARNING:
*
* This helper relies on the function prototype's toString() method to
* extract expected parameter names. When the client library is compiled
* with variable name obfuscation enabled, parameter names may not match the
* keys given in named argument dictionaries, and this function will not
* work UNLESS a goog.global['EXPORTED_FN_INFO'] map is available. Within this
* map, the value of goog.global['EXPORTED_FN_INFO'][fn.toString()] should be
* an object containing two unobfuscated keys:
*
* - 'name': The original unobfuscated name of fn.
* - 'paramNames': A list of unobfuscated parameter names expected by fn,
* with optional parameters prefixed by "opt_".
*
* The map should contain an entry for each that uses this helper.
* Typically that means public, exported, non-deprecated functions.
*
* @param {!Function} fn The function for which to extract arguments.
* @param {!Arguments} originalArgs The original arguments to the function.
* @return {!Object} A mapping from parameter names to defined argument values,
* if any. The keys are unobfuscated.
* @throws {!Error} If a required parameter is not specified, an unexpected
* parameter is provided, too many arguments are provided in sequence, or
* fails to extract parameter names.
*/
ee.arguments.extractFromFunction = function(fn, originalArgs) {
return ee.arguments.extractImpl_(
fn, originalArgs, ee.arguments.JS_PARAM_DECL_MATCHER_FUNCTION_);
Expand Down Expand Up @@ -45,7 +135,7 @@ ee.arguments.extractFromFunction = function(fn, originalArgs) {
* shall coorespond to that function instead of the class constructor. For
* example:
*
* class Animation {
* class Animation {
* // This is fine.
* oneconstructor(param1, param2) {...}
* // This is also fine.
Expand Down Expand Up @@ -250,7 +340,7 @@ ee.arguments.getParamNames_ = function(fn, parameterMatcher) {
}
} else {
// For un-compiled code, infer parameter names directly from the function
// body. Inspired by Angular's inferInjectionArgs():
// body. Inspired by Angular's inferInjectionArgs().
var fnStr = fn.toString().replace(ee.arguments.JS_COMMENT_MATCHER_, '');
const fnMatchResult = fnStr.match(parameterMatcher);
if (fnMatchResult === null) {
Expand Down
Loading

0 comments on commit 727d1d1

Please sign in to comment.