From a113ecfadeaacd14e9469ecbd9932b8b44fd9cbe Mon Sep 17 00:00:00 2001 From: Google Earth Engine Authors Date: Fri, 1 Dec 2023 01:20:22 +0000 Subject: [PATCH] v0.1.382 PiperOrigin-RevId: 586834943 --- javascript/build/ee_api_js.js | 10 +- javascript/build/ee_api_js_debug.js | 130 +++++++++++++++++------- javascript/build/ee_api_js_npm.js | 146 +++++++++++++++++--------- javascript/package.json | 2 +- javascript/src/apiclient.js | 2 +- python/ee/__init__.py | 31 +++--- python/ee/cli/commands.py | 41 +++++--- python/ee/data.py | 9 +- python/ee/oauth.py | 152 +++++++++++++++++++--------- python/ee/tests/algorithms.json | 9 ++ python/pyproject.toml | 2 +- 11 files changed, 367 insertions(+), 167 deletions(-) diff --git a/javascript/build/ee_api_js.js b/javascript/build/ee_api_js.js index 8df758224..41bda8eea 100644 --- a/javascript/build/ee_api_js.js +++ b/javascript/build/ee_api_js.js @@ -448,7 +448,7 @@ Wc(p,f[m]))});return b?b(l):l};return this.callback?(Gj(d,null,function(f,l){ret Hj.prototype.send=function(a,b){var c=[a.B+" "+a.path+" HTTP/1.1"];c.push("Content-Type: application/json; charset=utf-8");var d=Jj();null!=d&&c.push("Authorization: "+d);a=a.body?JSON.stringify(a.body):"";return[c.join("\r\n")+"\r\n\r\n"+a,b]}; var Kj=function(a,b,c){a=n(b.split("--"+a.split("; boundary=")[1]));for(b=a.next();!b.done;b=a.next())if(b=b.value.split("\r\n\r\n"),!(3>b.length)){var d=b[0].match(/\r\nContent-ID: ]*)>/)[1],e=Number(b[1].match(/^HTTP\S*\s(\d+)\s/)[1]);c(d,e,b.slice(2).join("\r\n\r\n"))}},Ej=function(){var a=Lj.replace(/\/api$/,"");return"window"in r&&!a.match(/^https?:\/\/content-/)?a.replace(/^(https?:\/\/)(.*\.googleapis\.com)$/,"$1content-$2"):a},Nj=function(a,b,c){var d=[];a&&(d=d.concat(Mj)); b&&d.push("https://www.googleapis.com/auth/devstorage.read_write");a=d=d.concat(c);c=b=0;for(var e={};cg)break;z++}return ik(C.status,function(Q){try{return C.getResponseHeader(Q)}catch(oa){return null}},C.responseText,l,void 0,e,d,f)},gk=function(a,b,c,d,e,g,f){var l=0,m={url:a,method:c,content:d,headers:e},p=ak,v=null!=g?g:10; m.callback=function(z){z=z.target;if(429==z.getStatus()&&la.y||a.y>=1<"); }; -var module$contents$safevalues$builders$html_builders_AttributeValue, module$contents$safevalues$builders$html_builders_TextOrHtml, module$contents$safevalues$builders$html_builders_VALID_TAG_OR_ATTRIBUTE_NAMES = /^[a-z][a-z\d-]*$/i, module$contents$safevalues$builders$html_builders_DISALLOWED_TAG_NAMES = "APPLET BASE EMBED IFRAME LINK MATH META OBJECT SCRIPT STYLE SVG TEMPLATE".split(" "), -module$contents$safevalues$builders$html_builders_VOID_TAG_NAMES = "AREA BR COL COMMAND HR IMG INPUT KEYGEN PARAM SOURCE TRACK WBR".split(" "), module$contents$safevalues$builders$html_builders_URL_ATTRIBUTES = ["action", "formaction", "href"]; +var module$contents$safevalues$builders$html_builders_TextOrHtml, module$contents$safevalues$builders$html_builders_VALID_TAG_OR_ATTRIBUTE_NAMES = /^[a-z][a-z\d-]*$/i, module$contents$safevalues$builders$html_builders_DISALLOWED_TAG_NAMES = "APPLET BASE EMBED IFRAME LINK MATH META OBJECT SCRIPT STYLE SVG TEMPLATE".split(" "); +module$exports$safevalues$builders$html_builders.VOID_TAG_NAMES = "AREA BR COL COMMAND HR IMG INPUT KEYGEN PARAM SOURCE TRACK WBR".split(" "); +var module$contents$safevalues$builders$html_builders_URL_ATTRIBUTES = ["action", "formaction", "href"]; function module$contents$safevalues$builders$html_builders_verifyTagName(tagName) { if (!module$contents$safevalues$builders$html_builders_VALID_TAG_OR_ATTRIBUTE_NAMES.test(tagName)) { throw Error(goog.DEBUG ? "Invalid tag name <" + tagName + ">." : ""); @@ -17853,12 +17823,17 @@ function module$contents$safevalues$builders$html_builders_verifyTagName(tagName throw Error(goog.DEBUG ? "Tag name <" + tagName + "> is not allowed for createHtml." : ""); } } +module$exports$safevalues$builders$html_builders.verifyTagName = module$contents$safevalues$builders$html_builders_verifyTagName; +function module$contents$safevalues$builders$html_builders_isVoidTag(tagName) { + return -1 !== module$exports$safevalues$builders$html_builders.VOID_TAG_NAMES.indexOf(tagName.toUpperCase()); +} +module$exports$safevalues$builders$html_builders.isVoidTag = module$contents$safevalues$builders$html_builders_isVoidTag; module$exports$safevalues$builders$html_builders.createHtml = function(tagName, attributes, content) { module$contents$safevalues$builders$html_builders_verifyTagName(tagName); var result = "<" + tagName; attributes && (result += module$contents$safevalues$builders$html_builders_stringifyAttributes(tagName, attributes)); Array.isArray(content) || (content = void 0 === content ? [] : [content]); - if (-1 !== module$contents$safevalues$builders$html_builders_VOID_TAG_NAMES.indexOf(tagName.toUpperCase())) { + if (module$contents$safevalues$builders$html_builders_isVoidTag(tagName)) { if (goog.DEBUG && 0 < content.length) { throw Error("Void tag <" + tagName + "> does not allow content."); } @@ -17881,6 +17856,7 @@ function module$contents$safevalues$builders$html_builders_stringifyAttributes(t } return result; } +module$exports$safevalues$builders$html_builders.stringifyAttributes = module$contents$safevalues$builders$html_builders_stringifyAttributes; function module$contents$safevalues$builders$html_builders_getAttrNameAndValue(tagName, name, value) { if (/^on/i.test(name)) { throw Error(goog.DEBUG ? 'Attribute "' + name + " is forbidden. Inline event handlers can lead to XSS. Please use the 'addEventListener' API instead." : ""); @@ -17891,6 +17867,82 @@ function module$contents$safevalues$builders$html_builders_getAttrNameAndValue(t } return name + '="' + module$contents$safevalues$builders$html_builders_htmlEscape(String(value)) + '"'; } +;var module$exports$safevalues$builders$html_formatter = {}, module$contents$safevalues$builders$html_formatter_module = module$contents$safevalues$builders$html_formatter_module || {id:"third_party/javascript/safevalues/builders/html_formatter.closure.js"}; +function module$contents$safevalues$builders$html_formatter_HtmlReplacement() { +} +function module$contents$safevalues$builders$html_formatter_StartTagReplacement() { +} +function module$contents$safevalues$builders$html_formatter_EndTagReplacement() { +} +var module$contents$safevalues$builders$html_formatter_Replacement; +module$exports$safevalues$builders$html_formatter.HtmlFormatter = function() { + this.replacements = new Map(); +}; +module$exports$safevalues$builders$html_formatter.HtmlFormatter.prototype.format = function(format) { + var $jscomp$this = this, openedTags = [], marker = (0,module$exports$safevalues$builders$html_builders.htmlEscape)("_safevalues_format_marker_:").toString(), html = (0,module$exports$safevalues$builders$html_builders.htmlEscape)(format).toString().replace(new RegExp("\\{" + marker + "[\\w&#;]+\\}", "g"), function(match) { + return $jscomp$this.replaceFormattingString(openedTags, match); + }); + if (0 !== openedTags.length) { + if (goog.DEBUG) { + throw Error("Expected no unclosed tags, got <" + openedTags.join(">, <") + ">."); + } + throw Error(); + } + return module$contents$safevalues$internals$html_impl_createHtmlInternal(html); +}; +module$exports$safevalues$builders$html_formatter.HtmlFormatter.prototype.replaceFormattingString = function(openedTags, match) { + var replacement = this.replacements.get(match); + if (!replacement) { + return match; + } + var result = ""; + switch(replacement.type) { + case "html": + result = replacement.html; + break; + case "startTag": + result = "<" + replacement.tagName + replacement.attributes + ">"; + goog.DEBUG && ((0,module$exports$safevalues$builders$html_builders.isVoidTag)(replacement.tagName.toLowerCase()) || openedTags.push(replacement.tagName.toLowerCase())); + break; + case "endTag": + result = ""; + if (goog.DEBUG) { + var lastTag = openedTags.pop(); + if (lastTag !== replacement.tagName.toLowerCase()) { + throw Error("Expected , got ."); + } + } + break; + default: + goog.DEBUG && module$contents$safevalues$builders$html_formatter_checkExhaustive(replacement, "type had an unknown value"); + } + return result; +}; +module$exports$safevalues$builders$html_formatter.HtmlFormatter.prototype.startTag = function(tagName, attributes) { + (0,module$exports$safevalues$builders$html_builders.verifyTagName)(tagName); + return this.storeReplacement({type:"startTag", tagName:tagName, attributes:void 0 !== attributes ? (0,module$exports$safevalues$builders$html_builders.stringifyAttributes)(tagName, attributes) : ""}); +}; +module$exports$safevalues$builders$html_formatter.HtmlFormatter.prototype.endTag = function(tagName) { + (0,module$exports$safevalues$builders$html_builders.verifyTagName)(tagName); + return this.storeReplacement({type:"endTag", tagName:tagName}); +}; +module$exports$safevalues$builders$html_formatter.HtmlFormatter.prototype.text = function(text) { + return this.storeReplacement({type:"html", html:(0,module$exports$safevalues$builders$html_builders.htmlEscape)(text).toString()}); +}; +module$exports$safevalues$builders$html_formatter.HtmlFormatter.prototype.safeHtml = function(safeHtml) { + return this.storeReplacement({type:"html", html:module$contents$safevalues$internals$html_impl_unwrapHtml(safeHtml).toString()}); +}; +module$exports$safevalues$builders$html_formatter.HtmlFormatter.prototype.storeReplacement = function(replacement) { + var marker = "{_safevalues_format_marker_:" + this.replacements.size + "_" + module$contents$safevalues$builders$html_formatter_getRandomString() + "}"; + this.replacements.set((0,module$exports$safevalues$builders$html_builders.htmlEscape)(marker).toString(), replacement); + return marker; +}; +function module$contents$safevalues$builders$html_formatter_getRandomString() { + return Math.random().toString(36).slice(2); +} +function module$contents$safevalues$builders$html_formatter_checkExhaustive(value, msg) { + throw Error(void 0 === msg ? "unexpected value " + value + "!" : msg); +} ;var module$contents$safevalues$dom$globals$range_module = module$contents$safevalues$dom$globals$range_module || {id:"third_party/javascript/safevalues/dom/globals/range.closure.js"}; function module$contents$safevalues$dom$globals$range_createContextualFragment(range, html) { return range.createContextualFragment(module$contents$safevalues$internals$html_impl_unwrapHtml(html)); @@ -18565,6 +18617,7 @@ module$exports$safevalues$index.isUrl = module$contents$safevalues$internals$url module$exports$safevalues$index.SafeUrl = goog.html.SafeUrl; module$exports$safevalues$index.unwrapUrl = module$contents$safevalues$internals$url_impl_unwrapUrl; module$exports$safevalues$index.reportOnlyHtmlPassthrough = module$contents$safevalues$reporting$reporting_reportOnlyHtmlPassthrough; +module$exports$safevalues$index.HtmlFormatter = module$exports$safevalues$builders$html_formatter.HtmlFormatter; module$exports$safevalues$index.createHtml = module$exports$safevalues$builders$html_builders.createHtml; module$exports$safevalues$index.safeStyleRule = module$exports$safevalues$builders$style_sheet_builders.safeStyleRule; safevalues.safeAttrPrefix = module$contents$safevalues$builders$attribute_builders_safeAttrPrefix; @@ -18629,13 +18682,14 @@ safevalues.isUrl = module$contents$safevalues$internals$url_impl_isUrl; safevalues.SafeUrl = goog.html.SafeUrl; safevalues.unwrapUrl = module$contents$safevalues$internals$url_impl_unwrapUrl; safevalues.reportOnlyHtmlPassthrough = module$contents$safevalues$reporting$reporting_reportOnlyHtmlPassthrough; +safevalues.HtmlFormatter = module$exports$safevalues$builders$html_formatter.HtmlFormatter; safevalues.createHtml = module$exports$safevalues$index.createHtml; safevalues.safeStyleRule = module$exports$safevalues$index.safeStyleRule; var $jscomp$templatelit$294235699$0 = $jscomp.createTemplateTagFirstArg(["https://apis.google.com/js/client.js?onload=", ""]); ee.apiclient = {}; var module$contents$ee$apiclient_apiclient = {}; ee.apiclient.VERSION = module$exports$ee$apiVersion.V1; -ee.apiclient.API_CLIENT_VERSION = "0.1.381"; +ee.apiclient.API_CLIENT_VERSION = "0.1.382"; ee.apiclient.NULL_VALUE = module$exports$eeapiclient$domain_object.NULL_VALUE; ee.apiclient.PromiseRequestService = module$exports$eeapiclient$promise_request_service.PromiseRequestService; ee.apiclient.MakeRequestParams = module$contents$eeapiclient$request_params_MakeRequestParams; @@ -18926,8 +18980,8 @@ module$contents$ee$apiclient_apiclient.send = function(path, params, callback, m var profileHookAtCallTime = module$contents$ee$apiclient_apiclient.profileHook_, contentType = "application/x-www-form-urlencoded"; body && (contentType = "application/json", method && method.startsWith("multipart") && (contentType = method, method = "POST")); method = method || "POST"; - var headers = {"Content-Type":contentType}, version = "0.1.381"; - "0.1.381" === version && (version = "latest"); + var headers = {"Content-Type":contentType}, version = "0.1.382"; + "0.1.382" === version && (version = "latest"); headers[module$contents$ee$apiclient_apiclient.API_CLIENT_VERSION_HEADER] = "ee-js/" + version; var authToken = module$contents$ee$apiclient_apiclient.getAuthToken(); if (null != authToken) { diff --git a/javascript/build/ee_api_js_npm.js b/javascript/build/ee_api_js_npm.js index c06424bd6..3332f1a9e 100644 --- a/javascript/build/ee_api_js_npm.js +++ b/javascript/build/ee_api_js_npm.js @@ -14679,37 +14679,6 @@ goog.dom.BrowserFeature.CAN_USE_CHILDREN_ATTRIBUTE = !0; goog.dom.BrowserFeature.CAN_USE_INNER_TEXT = !1; goog.dom.BrowserFeature.CAN_USE_PARENT_ELEMENT_PROPERTY = goog.userAgent.IE || goog.userAgent.WEBKIT; goog.dom.BrowserFeature.INNER_HTML_NEEDS_SCOPED_ELEMENT = goog.userAgent.IE; -goog.html.uncheckedconversions = {}; -goog.html.uncheckedconversions.safeHtmlFromStringKnownToSatisfyTypeContract = function(justification, html) { - goog.asserts.assertString(goog.string.Const.unwrap(justification), "must provide justification"); - goog.asserts.assert(!goog.string.internal.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)), "must provide non-empty justification"); - return module$contents$goog$html$SafeHtml_SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(html); -}; -goog.html.uncheckedconversions.safeScriptFromStringKnownToSatisfyTypeContract = function(justification, script) { - goog.asserts.assertString(goog.string.Const.unwrap(justification), "must provide justification"); - goog.asserts.assert(!goog.string.internal.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)), "must provide non-empty justification"); - return module$contents$goog$html$SafeScript_SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse(script); -}; -goog.html.uncheckedconversions.safeStyleFromStringKnownToSatisfyTypeContract = function(justification, style) { - goog.asserts.assertString(goog.string.Const.unwrap(justification), "must provide justification"); - goog.asserts.assert(!goog.string.internal.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)), "must provide non-empty justification"); - return module$contents$goog$html$SafeStyle_SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(style); -}; -goog.html.uncheckedconversions.safeStyleSheetFromStringKnownToSatisfyTypeContract = function(justification, styleSheet) { - goog.asserts.assertString(goog.string.Const.unwrap(justification), "must provide justification"); - goog.asserts.assert(!goog.string.internal.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)), "must provide non-empty justification"); - return module$contents$goog$html$SafeStyleSheet_SafeStyleSheet.createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(styleSheet); -}; -goog.html.uncheckedconversions.safeUrlFromStringKnownToSatisfyTypeContract = function(justification, url) { - goog.asserts.assertString(goog.string.Const.unwrap(justification), "must provide justification"); - goog.asserts.assert(!goog.string.internal.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)), "must provide non-empty justification"); - return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url); -}; -goog.html.uncheckedconversions.trustedResourceUrlFromStringKnownToSatisfyTypeContract = function(justification, url) { - goog.asserts.assertString(goog.string.Const.unwrap(justification), "must provide justification"); - goog.asserts.assert(!goog.string.internal.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)), "must provide non-empty justification"); - return goog.html.TrustedResourceUrl.createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(url); -}; goog.math.Coordinate = function(opt_x, opt_y) { this.x = void 0 !== opt_x ? opt_x : 0; this.y = void 0 !== opt_y ? opt_y : 0; @@ -15045,7 +15014,7 @@ goog.dom.createTable_ = function(doc, rows, columns, fillWithNbsp) { return table; }; goog.dom.constHtmlToNode = function(var_args) { - var stringArray = Array.prototype.map.call(arguments, goog.string.Const.unwrap), safeHtml = goog.html.uncheckedconversions.safeHtmlFromStringKnownToSatisfyTypeContract(goog.string.Const.from("Constant HTML string, that gets turned into a Node later, so it will be automatically balanced."), stringArray.join("")); + var stringArray = Array.prototype.map.call(arguments, goog.string.Const.unwrap), safeHtml = module$contents$safevalues$restricted$reviewed_htmlSafeByReview(stringArray.join(""), "Constant HTML string, that gets turned into a Node later, so it will be automatically balanced."); return goog.dom.safeHtmlToNode(safeHtml); }; goog.dom.safeHtmlToNode = function(html) { @@ -17843,8 +17812,9 @@ module$exports$safevalues$builders$html_builders.joinHtmls = module$contents$saf module$exports$safevalues$builders$html_builders.doctypeHtml = function() { return module$contents$safevalues$internals$html_impl_createHtmlInternal(""); }; -var module$contents$safevalues$builders$html_builders_AttributeValue, module$contents$safevalues$builders$html_builders_TextOrHtml, module$contents$safevalues$builders$html_builders_VALID_TAG_OR_ATTRIBUTE_NAMES = /^[a-z][a-z\d-]*$/i, module$contents$safevalues$builders$html_builders_DISALLOWED_TAG_NAMES = "APPLET BASE EMBED IFRAME LINK MATH META OBJECT SCRIPT STYLE SVG TEMPLATE".split(" "), -module$contents$safevalues$builders$html_builders_VOID_TAG_NAMES = "AREA BR COL COMMAND HR IMG INPUT KEYGEN PARAM SOURCE TRACK WBR".split(" "), module$contents$safevalues$builders$html_builders_URL_ATTRIBUTES = ["action", "formaction", "href"]; +var module$contents$safevalues$builders$html_builders_TextOrHtml, module$contents$safevalues$builders$html_builders_VALID_TAG_OR_ATTRIBUTE_NAMES = /^[a-z][a-z\d-]*$/i, module$contents$safevalues$builders$html_builders_DISALLOWED_TAG_NAMES = "APPLET BASE EMBED IFRAME LINK MATH META OBJECT SCRIPT STYLE SVG TEMPLATE".split(" "); +module$exports$safevalues$builders$html_builders.VOID_TAG_NAMES = "AREA BR COL COMMAND HR IMG INPUT KEYGEN PARAM SOURCE TRACK WBR".split(" "); +var module$contents$safevalues$builders$html_builders_URL_ATTRIBUTES = ["action", "formaction", "href"]; function module$contents$safevalues$builders$html_builders_verifyTagName(tagName) { if (!module$contents$safevalues$builders$html_builders_VALID_TAG_OR_ATTRIBUTE_NAMES.test(tagName)) { throw Error(goog.DEBUG ? "Invalid tag name <" + tagName + ">." : ""); @@ -17853,12 +17823,17 @@ function module$contents$safevalues$builders$html_builders_verifyTagName(tagName throw Error(goog.DEBUG ? "Tag name <" + tagName + "> is not allowed for createHtml." : ""); } } +module$exports$safevalues$builders$html_builders.verifyTagName = module$contents$safevalues$builders$html_builders_verifyTagName; +function module$contents$safevalues$builders$html_builders_isVoidTag(tagName) { + return -1 !== module$exports$safevalues$builders$html_builders.VOID_TAG_NAMES.indexOf(tagName.toUpperCase()); +} +module$exports$safevalues$builders$html_builders.isVoidTag = module$contents$safevalues$builders$html_builders_isVoidTag; module$exports$safevalues$builders$html_builders.createHtml = function(tagName, attributes, content) { module$contents$safevalues$builders$html_builders_verifyTagName(tagName); var result = "<" + tagName; attributes && (result += module$contents$safevalues$builders$html_builders_stringifyAttributes(tagName, attributes)); Array.isArray(content) || (content = void 0 === content ? [] : [content]); - if (-1 !== module$contents$safevalues$builders$html_builders_VOID_TAG_NAMES.indexOf(tagName.toUpperCase())) { + if (module$contents$safevalues$builders$html_builders_isVoidTag(tagName)) { if (goog.DEBUG && 0 < content.length) { throw Error("Void tag <" + tagName + "> does not allow content."); } @@ -17881,6 +17856,7 @@ function module$contents$safevalues$builders$html_builders_stringifyAttributes(t } return result; } +module$exports$safevalues$builders$html_builders.stringifyAttributes = module$contents$safevalues$builders$html_builders_stringifyAttributes; function module$contents$safevalues$builders$html_builders_getAttrNameAndValue(tagName, name, value) { if (/^on/i.test(name)) { throw Error(goog.DEBUG ? 'Attribute "' + name + " is forbidden. Inline event handlers can lead to XSS. Please use the 'addEventListener' API instead." : ""); @@ -17891,6 +17867,82 @@ function module$contents$safevalues$builders$html_builders_getAttrNameAndValue(t } return name + '="' + module$contents$safevalues$builders$html_builders_htmlEscape(String(value)) + '"'; } +;var module$exports$safevalues$builders$html_formatter = {}, module$contents$safevalues$builders$html_formatter_module = module$contents$safevalues$builders$html_formatter_module || {id:"third_party/javascript/safevalues/builders/html_formatter.closure.js"}; +function module$contents$safevalues$builders$html_formatter_HtmlReplacement() { +} +function module$contents$safevalues$builders$html_formatter_StartTagReplacement() { +} +function module$contents$safevalues$builders$html_formatter_EndTagReplacement() { +} +var module$contents$safevalues$builders$html_formatter_Replacement; +module$exports$safevalues$builders$html_formatter.HtmlFormatter = function() { + this.replacements = new Map(); +}; +module$exports$safevalues$builders$html_formatter.HtmlFormatter.prototype.format = function(format) { + var $jscomp$this = this, openedTags = [], marker = (0,module$exports$safevalues$builders$html_builders.htmlEscape)("_safevalues_format_marker_:").toString(), html = (0,module$exports$safevalues$builders$html_builders.htmlEscape)(format).toString().replace(new RegExp("\\{" + marker + "[\\w&#;]+\\}", "g"), function(match) { + return $jscomp$this.replaceFormattingString(openedTags, match); + }); + if (0 !== openedTags.length) { + if (goog.DEBUG) { + throw Error("Expected no unclosed tags, got <" + openedTags.join(">, <") + ">."); + } + throw Error(); + } + return module$contents$safevalues$internals$html_impl_createHtmlInternal(html); +}; +module$exports$safevalues$builders$html_formatter.HtmlFormatter.prototype.replaceFormattingString = function(openedTags, match) { + var replacement = this.replacements.get(match); + if (!replacement) { + return match; + } + var result = ""; + switch(replacement.type) { + case "html": + result = replacement.html; + break; + case "startTag": + result = "<" + replacement.tagName + replacement.attributes + ">"; + goog.DEBUG && ((0,module$exports$safevalues$builders$html_builders.isVoidTag)(replacement.tagName.toLowerCase()) || openedTags.push(replacement.tagName.toLowerCase())); + break; + case "endTag": + result = ""; + if (goog.DEBUG) { + var lastTag = openedTags.pop(); + if (lastTag !== replacement.tagName.toLowerCase()) { + throw Error("Expected , got ."); + } + } + break; + default: + goog.DEBUG && module$contents$safevalues$builders$html_formatter_checkExhaustive(replacement, "type had an unknown value"); + } + return result; +}; +module$exports$safevalues$builders$html_formatter.HtmlFormatter.prototype.startTag = function(tagName, attributes) { + (0,module$exports$safevalues$builders$html_builders.verifyTagName)(tagName); + return this.storeReplacement({type:"startTag", tagName:tagName, attributes:void 0 !== attributes ? (0,module$exports$safevalues$builders$html_builders.stringifyAttributes)(tagName, attributes) : ""}); +}; +module$exports$safevalues$builders$html_formatter.HtmlFormatter.prototype.endTag = function(tagName) { + (0,module$exports$safevalues$builders$html_builders.verifyTagName)(tagName); + return this.storeReplacement({type:"endTag", tagName:tagName}); +}; +module$exports$safevalues$builders$html_formatter.HtmlFormatter.prototype.text = function(text) { + return this.storeReplacement({type:"html", html:(0,module$exports$safevalues$builders$html_builders.htmlEscape)(text).toString()}); +}; +module$exports$safevalues$builders$html_formatter.HtmlFormatter.prototype.safeHtml = function(safeHtml) { + return this.storeReplacement({type:"html", html:module$contents$safevalues$internals$html_impl_unwrapHtml(safeHtml).toString()}); +}; +module$exports$safevalues$builders$html_formatter.HtmlFormatter.prototype.storeReplacement = function(replacement) { + var marker = "{_safevalues_format_marker_:" + this.replacements.size + "_" + module$contents$safevalues$builders$html_formatter_getRandomString() + "}"; + this.replacements.set((0,module$exports$safevalues$builders$html_builders.htmlEscape)(marker).toString(), replacement); + return marker; +}; +function module$contents$safevalues$builders$html_formatter_getRandomString() { + return Math.random().toString(36).slice(2); +} +function module$contents$safevalues$builders$html_formatter_checkExhaustive(value, msg) { + throw Error(void 0 === msg ? "unexpected value " + value + "!" : msg); +} ;var module$contents$safevalues$dom$globals$range_module = module$contents$safevalues$dom$globals$range_module || {id:"third_party/javascript/safevalues/dom/globals/range.closure.js"}; function module$contents$safevalues$dom$globals$range_createContextualFragment(range, html) { return range.createContextualFragment(module$contents$safevalues$internals$html_impl_unwrapHtml(html)); @@ -18565,6 +18617,7 @@ module$exports$safevalues$index.isUrl = module$contents$safevalues$internals$url module$exports$safevalues$index.SafeUrl = goog.html.SafeUrl; module$exports$safevalues$index.unwrapUrl = module$contents$safevalues$internals$url_impl_unwrapUrl; module$exports$safevalues$index.reportOnlyHtmlPassthrough = module$contents$safevalues$reporting$reporting_reportOnlyHtmlPassthrough; +module$exports$safevalues$index.HtmlFormatter = module$exports$safevalues$builders$html_formatter.HtmlFormatter; module$exports$safevalues$index.createHtml = module$exports$safevalues$builders$html_builders.createHtml; module$exports$safevalues$index.safeStyleRule = module$exports$safevalues$builders$style_sheet_builders.safeStyleRule; safevalues.safeAttrPrefix = module$contents$safevalues$builders$attribute_builders_safeAttrPrefix; @@ -18629,13 +18682,14 @@ safevalues.isUrl = module$contents$safevalues$internals$url_impl_isUrl; safevalues.SafeUrl = goog.html.SafeUrl; safevalues.unwrapUrl = module$contents$safevalues$internals$url_impl_unwrapUrl; safevalues.reportOnlyHtmlPassthrough = module$contents$safevalues$reporting$reporting_reportOnlyHtmlPassthrough; +safevalues.HtmlFormatter = module$exports$safevalues$builders$html_formatter.HtmlFormatter; safevalues.createHtml = module$exports$safevalues$index.createHtml; safevalues.safeStyleRule = module$exports$safevalues$index.safeStyleRule; var $jscomp$templatelit$294235699$0 = $jscomp.createTemplateTagFirstArg(["https://apis.google.com/js/client.js?onload=", ""]); ee.apiclient = {}; var module$contents$ee$apiclient_apiclient = {}; ee.apiclient.VERSION = module$exports$ee$apiVersion.V1; -ee.apiclient.API_CLIENT_VERSION = "0.1.381"; +ee.apiclient.API_CLIENT_VERSION = "0.1.382"; ee.apiclient.NULL_VALUE = module$exports$eeapiclient$domain_object.NULL_VALUE; ee.apiclient.PromiseRequestService = module$exports$eeapiclient$promise_request_service.PromiseRequestService; ee.apiclient.MakeRequestParams = module$contents$eeapiclient$request_params_MakeRequestParams; @@ -18926,8 +18980,8 @@ module$contents$ee$apiclient_apiclient.send = function(path, params, callback, m var profileHookAtCallTime = module$contents$ee$apiclient_apiclient.profileHook_, contentType = "application/x-www-form-urlencoded"; body && (contentType = "application/json", method && method.startsWith("multipart") && (contentType = method, method = "POST")); method = method || "POST"; - var headers = {"Content-Type":contentType}, version = "0.1.381"; - "0.1.381" === version && (version = "latest"); + var headers = {"Content-Type":contentType}, version = "0.1.382"; + "0.1.382" === version && (version = "latest"); headers[module$contents$ee$apiclient_apiclient.API_CLIENT_VERSION_HEADER] = "ee-js/" + version; var authToken = module$contents$ee$apiclient_apiclient.getAuthToken(); if (null != authToken) { @@ -26484,14 +26538,14 @@ ee.data.Profiler.Format.prototype.toString = function() { ee.data.Profiler.Format.TEXT = new ee.data.Profiler.Format("text"); ee.data.Profiler.Format.JSON = new ee.data.Profiler.Format("json"); (function() { - var exportedFnInfo = {}, orderedFnNames = "ee.ApiFunction.lookup ee.ApiFunction._apply ee.ApiFunction._call ee.batch.Export.videoMap.toCloudStorage ee.batch.Export.table.toDrive ee.batch.Export.table.toBigQuery ee.batch.Export.video.toCloudStorage ee.batch.Export.video.toDrive ee.batch.Export.image.toCloudStorage ee.batch.Export.image.toAsset ee.batch.Export.table.toCloudStorage ee.batch.Export.table.toAsset ee.batch.Export.table.toFeatureView ee.batch.Export.map.toCloudStorage ee.batch.Export.image.toDrive ee.batch.Export.classifier.toAsset ee.Collection.prototype.map ee.Collection.prototype.filterBounds ee.Collection.prototype.filter ee.Collection.prototype.filterDate ee.Collection.prototype.iterate ee.Collection.prototype.filterMetadata ee.Collection.prototype.limit ee.Collection.prototype.sort ee.ComputedObject.prototype.serialize ee.ComputedObject.prototype.evaluate ee.ComputedObject.prototype.aside ee.ComputedObject.prototype.getInfo ee.data.deleteAsset ee.data.createAssetHome ee.data.getList ee.data.listAssets ee.data.createAsset ee.data.authenticateViaOauth ee.data.getTaskStatus ee.data.getTableDownloadId ee.data.getFeatureViewTilesKey ee.data.getDownloadId ee.data.makeDownloadUrl ee.data.getInfo ee.data.getAssetRootQuota ee.data.computeValue ee.data.listFeatures ee.data.setAssetProperties ee.data.getThumbId ee.data.authenticateViaPopup ee.data.getAssetAcl ee.data.getVideoThumbId ee.data.getFilmstripThumbId ee.data.makeThumbUrl ee.data.resetWorkloadTag ee.data.authenticateViaPrivateKey ee.data.getAssetRoots ee.data.listImages ee.data.setAssetAcl ee.data.listBuckets ee.data.cancelOperation ee.data.listOperations ee.data.getTaskList ee.data.cancelTask ee.data.setDefaultWorkloadTag ee.data.copyAsset ee.data.updateAsset ee.data.getTaskListWithLimit ee.data.getAsset ee.data.renameAsset ee.data.newTaskId ee.data.createFolder ee.data.makeTableDownloadUrl ee.data.authenticate ee.data.getOperation ee.data.getMapId ee.data.startTableIngestion ee.data.setWorkloadTag ee.data.startProcessing ee.data.updateTask ee.data.getTileUrl ee.data.getWorkloadTag ee.data.startIngestion ee.Date ee.Deserializer.fromCloudApiJSON ee.Deserializer.decodeCloudApi ee.Deserializer.decode ee.Deserializer.fromJSON ee.Dictionary ee.TILE_SIZE ee.reset ee.initialize ee.call ee.Algorithms ee.InitState ee.apply ee.Element.prototype.set ee.Encodable.SourceFrame ee.Feature ee.Feature.prototype.getMapId ee.Feature.prototype.getMap ee.Feature.prototype.getInfo ee.FeatureCollection.prototype.getMapId ee.FeatureCollection.prototype.getDownloadURL ee.FeatureCollection.prototype.select ee.FeatureCollection.prototype.getInfo ee.FeatureCollection ee.FeatureCollection.prototype.getMap ee.Filter.lt ee.Filter.and ee.Filter.or ee.Filter.gte ee.Filter.date ee.Filter.inList ee.Filter.prototype.not ee.Filter ee.Filter.metadata ee.Filter.eq ee.Filter.neq ee.Filter.bounds ee.Filter.gt ee.Filter.lte ee.Function.prototype.apply ee.Function.prototype.call ee.Geometry.Polygon ee.Geometry.prototype.toGeoJSON ee.Geometry.prototype.toGeoJSONString ee.Geometry.BBox ee.Geometry.LinearRing ee.Geometry.Point ee.Geometry.prototype.serialize ee.Geometry.MultiPolygon ee.Geometry ee.Geometry.MultiPoint ee.Geometry.MultiLineString ee.Geometry.Rectangle ee.Geometry.LineString ee.Image.prototype.getMap ee.Image.prototype.rename ee.Image.prototype.getInfo ee.Image.prototype.expression ee.Image.prototype.getThumbId ee.Image.rgb ee.Image.prototype.getDownloadURL ee.Image.prototype.getThumbURL ee.Image.prototype.clip ee.Image.prototype.select ee.Image.cat ee.Image ee.Image.prototype.getMapId ee.ImageCollection.prototype.getMapId ee.ImageCollection.prototype.getMap ee.ImageCollection.prototype.select ee.ImageCollection.prototype.getInfo ee.ImageCollection.prototype.linkCollection ee.ImageCollection.prototype.first ee.ImageCollection ee.ImageCollection.prototype.getFilmstripThumbURL ee.ImageCollection.prototype.getVideoThumbURL ee.List ee.Number ee.Serializer.encodeCloudApi ee.Serializer.toReadableJSON ee.Serializer.toCloudApiJSON ee.Serializer.toReadableCloudApiJSON ee.Serializer.encode ee.Serializer.toJSON ee.Serializer.encodeCloudApiPretty ee.String ee.Terrain".split(" "), + var exportedFnInfo = {}, orderedFnNames = "ee.ApiFunction.lookup ee.ApiFunction._apply ee.ApiFunction._call ee.batch.Export.videoMap.toCloudStorage ee.batch.Export.table.toDrive ee.batch.Export.table.toBigQuery ee.batch.Export.video.toCloudStorage ee.batch.Export.video.toDrive ee.batch.Export.image.toCloudStorage ee.batch.Export.image.toAsset ee.batch.Export.table.toCloudStorage ee.batch.Export.table.toAsset ee.batch.Export.table.toFeatureView ee.batch.Export.map.toCloudStorage ee.batch.Export.image.toDrive ee.batch.Export.classifier.toAsset ee.Collection.prototype.map ee.Collection.prototype.filterBounds ee.Collection.prototype.filter ee.Collection.prototype.filterDate ee.Collection.prototype.iterate ee.Collection.prototype.filterMetadata ee.Collection.prototype.limit ee.Collection.prototype.sort ee.ComputedObject.prototype.serialize ee.ComputedObject.prototype.evaluate ee.ComputedObject.prototype.aside ee.ComputedObject.prototype.getInfo ee.data.deleteAsset ee.data.createAssetHome ee.data.getList ee.data.listAssets ee.data.createAsset ee.data.authenticateViaOauth ee.data.getTableDownloadId ee.data.getFeatureViewTilesKey ee.data.getDownloadId ee.data.makeDownloadUrl ee.data.getInfo ee.data.getAssetRootQuota ee.data.computeValue ee.data.listFeatures ee.data.setAssetProperties ee.data.getThumbId ee.data.authenticateViaPopup ee.data.getAssetAcl ee.data.getVideoThumbId ee.data.getFilmstripThumbId ee.data.makeThumbUrl ee.data.resetWorkloadTag ee.data.authenticateViaPrivateKey ee.data.getAssetRoots ee.data.listImages ee.data.setAssetAcl ee.data.listBuckets ee.data.cancelOperation ee.data.listOperations ee.data.getTaskList ee.data.cancelTask ee.data.setDefaultWorkloadTag ee.data.copyAsset ee.data.updateAsset ee.data.getTaskListWithLimit ee.data.getAsset ee.data.renameAsset ee.data.newTaskId ee.data.createFolder ee.data.makeTableDownloadUrl ee.data.getTaskStatus ee.data.authenticate ee.data.getOperation ee.data.getMapId ee.data.startTableIngestion ee.data.setWorkloadTag ee.data.startProcessing ee.data.updateTask ee.data.getTileUrl ee.data.getWorkloadTag ee.data.startIngestion ee.Date ee.Deserializer.fromCloudApiJSON ee.Deserializer.decodeCloudApi ee.Deserializer.decode ee.Deserializer.fromJSON ee.Dictionary ee.TILE_SIZE ee.reset ee.initialize ee.call ee.Algorithms ee.InitState ee.apply ee.Element.prototype.set ee.Encodable.SourceFrame ee.Feature ee.Feature.prototype.getMapId ee.Feature.prototype.getMap ee.Feature.prototype.getInfo ee.FeatureCollection.prototype.getMapId ee.FeatureCollection.prototype.getDownloadURL ee.FeatureCollection.prototype.select ee.FeatureCollection.prototype.getInfo ee.FeatureCollection ee.FeatureCollection.prototype.getMap ee.Filter.lt ee.Filter.and ee.Filter.or ee.Filter.gte ee.Filter.date ee.Filter.inList ee.Filter.prototype.not ee.Filter ee.Filter.metadata ee.Filter.eq ee.Filter.neq ee.Filter.bounds ee.Filter.gt ee.Filter.lte ee.Function.prototype.apply ee.Function.prototype.call ee.Geometry.Polygon ee.Geometry.prototype.toGeoJSON ee.Geometry.prototype.toGeoJSONString ee.Geometry.BBox ee.Geometry.LinearRing ee.Geometry.Point ee.Geometry.prototype.serialize ee.Geometry.MultiPolygon ee.Geometry ee.Geometry.MultiPoint ee.Geometry.MultiLineString ee.Geometry.Rectangle ee.Geometry.LineString ee.Image.prototype.getMap ee.Image.prototype.rename ee.Image.prototype.getInfo ee.Image.prototype.expression ee.Image.prototype.getThumbId ee.Image.rgb ee.Image.prototype.getDownloadURL ee.Image.prototype.getThumbURL ee.Image.prototype.clip ee.Image.prototype.select ee.Image.cat ee.Image ee.Image.prototype.getMapId ee.ImageCollection.prototype.getMapId ee.ImageCollection.prototype.getMap ee.ImageCollection.prototype.select ee.ImageCollection.prototype.getInfo ee.ImageCollection.prototype.linkCollection ee.ImageCollection.prototype.first ee.ImageCollection ee.ImageCollection.prototype.getFilmstripThumbURL ee.ImageCollection.prototype.getVideoThumbURL ee.List ee.Number ee.Serializer.encodeCloudApi ee.Serializer.toReadableJSON ee.Serializer.toCloudApiJSON ee.Serializer.toReadableCloudApiJSON ee.Serializer.encode ee.Serializer.toJSON ee.Serializer.encodeCloudApiPretty ee.String ee.Terrain".split(" "), orderedParamLists = [["name"], ["name", "namedArgs"], ["name", "var_args"], "collection opt_description opt_bucket opt_fileNamePrefix opt_framesPerSecond opt_writePublicTiles opt_minZoom opt_maxZoom opt_scale opt_region opt_skipEmptyTiles opt_minTimeMachineZoomSubset opt_maxTimeMachineZoomSubset opt_tileWidth opt_tileHeight opt_tileStride opt_videoFormat opt_version opt_mapsApiKey opt_bucketCorsUris".split(" "), "collection opt_description opt_folder opt_fileNamePrefix opt_fileFormat opt_selectors opt_maxVertices".split(" "), "collection opt_description opt_table opt_overwrite opt_append opt_selectors opt_maxVertices".split(" "), "collection opt_description opt_bucket opt_fileNamePrefix opt_framesPerSecond opt_dimensions opt_region opt_scale opt_crs opt_crsTransform opt_maxPixels opt_maxFrames".split(" "), "collection opt_description opt_folder opt_fileNamePrefix opt_framesPerSecond opt_dimensions opt_region opt_scale opt_crs opt_crsTransform opt_maxPixels opt_maxFrames".split(" "), "image opt_description opt_bucket opt_fileNamePrefix opt_dimensions opt_region opt_scale opt_crs opt_crsTransform opt_maxPixels opt_shardSize opt_fileDimensions opt_skipEmptyTiles opt_fileFormat opt_formatOptions".split(" "), "image opt_description opt_assetId opt_pyramidingPolicy opt_dimensions opt_region opt_scale opt_crs opt_crsTransform opt_maxPixels opt_shardSize".split(" "), "collection opt_description opt_bucket opt_fileNamePrefix opt_fileFormat opt_selectors opt_maxVertices".split(" "), ["collection", "opt_description", "opt_assetId", "opt_maxVertices"], "collection opt_description opt_assetId opt_maxFeaturesPerTile opt_thinningStrategy opt_thinningRanking opt_zOrderRanking".split(" "), "image opt_description opt_bucket opt_fileFormat opt_path opt_writePublicTiles opt_scale opt_maxZoom opt_minZoom opt_region opt_skipEmptyTiles opt_mapsApiKey opt_bucketCorsUris".split(" "), "image opt_description opt_folder opt_fileNamePrefix opt_dimensions opt_region opt_scale opt_crs opt_crsTransform opt_maxPixels opt_shardSize opt_fileDimensions opt_skipEmptyTiles opt_fileFormat opt_formatOptions".split(" "), ["classifier", "opt_description", "opt_assetId"], ["algorithm", "opt_dropNulls"], ["geometry"], ["filter"], ["start", "opt_end"], ["algorithm", "opt_first"], ["name", "operator", "value"], ["max", "opt_property", "opt_ascending"], ["property", "opt_ascending"], ["legacy"], - ["callback"], ["func", "var_args"], ["opt_callback"], ["assetId", "opt_callback"], ["requestedId", "opt_callback"], ["params", "opt_callback"], ["parent", "opt_params", "opt_callback"], ["value", "opt_path", "opt_force", "opt_properties", "opt_callback"], "clientId success opt_error opt_extraScopes opt_onImmediateFailed opt_suppressDefaultScopes".split(" "), ["taskId", "opt_callback"], ["params", "opt_callback"], ["params", "opt_callback"], ["params", "opt_callback"], ["id"], ["id", "opt_callback"], - ["rootId", "opt_callback"], ["obj", "opt_callback"], ["asset", "params", "opt_callback"], ["assetId", "properties", "opt_callback"], ["params", "opt_callback"], ["opt_success", "opt_error"], ["assetId", "opt_callback"], ["params", "opt_callback"], ["params", "opt_callback"], ["id"], ["opt_resetDefault"], ["privateKey", "opt_success", "opt_error", "opt_extraScopes", "opt_suppressDefaultScopes"], ["opt_callback"], ["parent", "opt_params", "opt_callback"], ["assetId", "aclUpdate", "opt_callback"], - ["project", "opt_callback"], ["operationName", "opt_callback"], ["opt_limit", "opt_callback"], ["opt_callback"], ["taskId", "opt_callback"], ["tag"], ["sourceId", "destinationId", "opt_overwrite", "opt_callback"], ["assetId", "asset", "updateFields", "opt_callback"], ["opt_limit", "opt_callback"], ["id", "opt_callback"], ["sourceId", "destinationId", "opt_callback"], ["opt_count", "opt_callback"], ["path", "opt_force", "opt_callback"], ["id"], ["clientId", "success", "opt_error", "opt_extraScopes", + ["callback"], ["func", "var_args"], ["opt_callback"], ["assetId", "opt_callback"], ["requestedId", "opt_callback"], ["params", "opt_callback"], ["parent", "opt_params", "opt_callback"], ["value", "opt_path", "opt_force", "opt_properties", "opt_callback"], "clientId success opt_error opt_extraScopes opt_onImmediateFailed opt_suppressDefaultScopes".split(" "), ["params", "opt_callback"], ["params", "opt_callback"], ["params", "opt_callback"], ["id"], ["id", "opt_callback"], ["rootId", "opt_callback"], + ["obj", "opt_callback"], ["asset", "params", "opt_callback"], ["assetId", "properties", "opt_callback"], ["params", "opt_callback"], ["opt_success", "opt_error"], ["assetId", "opt_callback"], ["params", "opt_callback"], ["params", "opt_callback"], ["id"], ["opt_resetDefault"], ["privateKey", "opt_success", "opt_error", "opt_extraScopes", "opt_suppressDefaultScopes"], ["opt_callback"], ["parent", "opt_params", "opt_callback"], ["assetId", "aclUpdate", "opt_callback"], ["project", "opt_callback"], + ["operationName", "opt_callback"], ["opt_limit", "opt_callback"], ["opt_callback"], ["taskId", "opt_callback"], ["tag"], ["sourceId", "destinationId", "opt_overwrite", "opt_callback"], ["assetId", "asset", "updateFields", "opt_callback"], ["opt_limit", "opt_callback"], ["id", "opt_callback"], ["sourceId", "destinationId", "opt_callback"], ["opt_count", "opt_callback"], ["path", "opt_force", "opt_callback"], ["id"], ["taskId", "opt_callback"], ["clientId", "success", "opt_error", "opt_extraScopes", "opt_onImmediateFailed"], ["operationName", "opt_callback"], ["params", "opt_callback"], ["taskId", "request", "opt_callback"], ["tag"], ["taskId", "params", "opt_callback"], ["taskId", "action", "opt_callback"], ["id", "x", "y", "z"], [], ["taskId", "request", "opt_callback"], ["date", "opt_tz"], ["json"], ["json"], ["json"], ["json"], ["opt_dict"], [], [], "opt_baseurl opt_tileurl opt_successCallback opt_errorCallback opt_xsrfToken opt_project".split(" "), ["func", "var_args"], [], [], ["func", "namedArgs"], ["var_args"], [], ["geometry", "opt_properties"], ["opt_visParams", "opt_callback"], ["opt_visParams", "opt_callback"], ["opt_callback"], ["opt_visParams", "opt_callback"], ["opt_format", "opt_selectors", "opt_filename", "opt_callback"], ["propertySelectors", "opt_newProperties", "opt_retainGeometry"], ["opt_callback"], ["args", "opt_column"], ["opt_visParams", "opt_callback"], ["name", "value"], ["var_args"], ["var_args"], ["name", "value"], ["start", "opt_end"], ["opt_leftField", "opt_rightValue", "opt_rightField", "opt_leftValue"], [], ["opt_filter"], ["name", "operator", "value"], ["name", "value"], ["name", "value"], ["geometry", "opt_errorMargin"], ["name", "value"], ["name", "value"], ["namedArgs"], ["var_args"], ["coords", "opt_proj", "opt_geodesic", "opt_maxError", "opt_evenOdd"], [], [], ["west", "south", "east", "north"], ["coords", "opt_proj", "opt_geodesic", "opt_maxError"], ["coords", "opt_proj"], ["legacy"], ["coords", "opt_proj", "opt_geodesic", "opt_maxError", @@ -26499,10 +26553,10 @@ ee.data.Profiler.Format.JSON = new ee.data.Profiler.Format("json"); ["opt_args"], ["opt_visParams", "opt_callback"], ["opt_visParams", "opt_callback"], ["opt_visParams", "opt_callback"], ["selectors", "opt_names"], ["opt_callback"], ["imageCollection", "opt_linkedBands", "opt_linkedProperties", "opt_matchPropertyName"], [], ["args"], ["params", "opt_callback"], ["params", "opt_callback"], ["list"], ["number"], ["obj"], ["obj"], ["obj"], ["obj"], ["obj", "opt_isCompound"], ["obj"], ["obj"], ["string"], []]; [ee.ApiFunction.lookup, ee.ApiFunction._apply, ee.ApiFunction._call, module$contents$ee$batch_Export.videoMap.toCloudStorage, module$contents$ee$batch_Export.table.toDrive, module$contents$ee$batch_Export.table.toBigQuery, module$contents$ee$batch_Export.video.toCloudStorage, module$contents$ee$batch_Export.video.toDrive, module$contents$ee$batch_Export.image.toCloudStorage, module$contents$ee$batch_Export.image.toAsset, module$contents$ee$batch_Export.table.toCloudStorage, module$contents$ee$batch_Export.table.toAsset, module$contents$ee$batch_Export.table.toFeatureView, module$contents$ee$batch_Export.map.toCloudStorage, module$contents$ee$batch_Export.image.toDrive, module$contents$ee$batch_Export.classifier.toAsset, ee.Collection.prototype.map, ee.Collection.prototype.filterBounds, ee.Collection.prototype.filter, ee.Collection.prototype.filterDate, ee.Collection.prototype.iterate, ee.Collection.prototype.filterMetadata, ee.Collection.prototype.limit, ee.Collection.prototype.sort, ee.ComputedObject.prototype.serialize, - ee.ComputedObject.prototype.evaluate, ee.ComputedObject.prototype.aside, ee.ComputedObject.prototype.getInfo, ee.data.deleteAsset, ee.data.createAssetHome, ee.data.getList, ee.data.listAssets, ee.data.createAsset, ee.data.authenticateViaOauth, ee.data.getTaskStatus, ee.data.getTableDownloadId, ee.data.getFeatureViewTilesKey, ee.data.getDownloadId, ee.data.makeDownloadUrl, ee.data.getInfo, ee.data.getAssetRootQuota, ee.data.computeValue, ee.data.listFeatures, ee.data.setAssetProperties, ee.data.getThumbId, - ee.data.authenticateViaPopup, ee.data.getAssetAcl, ee.data.getVideoThumbId, ee.data.getFilmstripThumbId, ee.data.makeThumbUrl, ee.data.resetWorkloadTag, ee.data.authenticateViaPrivateKey, ee.data.getAssetRoots, ee.data.listImages, ee.data.setAssetAcl, ee.data.listBuckets, ee.data.cancelOperation, ee.data.listOperations, ee.data.getTaskList, ee.data.cancelTask, ee.data.setDefaultWorkloadTag, ee.data.copyAsset, ee.data.updateAsset, ee.data.getTaskListWithLimit, ee.data.getAsset, ee.data.renameAsset, - ee.data.newTaskId, ee.data.createFolder, ee.data.makeTableDownloadUrl, ee.data.authenticate, ee.data.getOperation, ee.data.getMapId, ee.data.startTableIngestion, ee.data.setWorkloadTag, ee.data.startProcessing, ee.data.updateTask, ee.data.getTileUrl, ee.data.getWorkloadTag, ee.data.startIngestion, ee.Date, ee.Deserializer.fromCloudApiJSON, ee.Deserializer.decodeCloudApi, ee.Deserializer.decode, ee.Deserializer.fromJSON, ee.Dictionary, ee.TILE_SIZE, ee.reset, ee.initialize, ee.call, ee.Algorithms, - ee.InitState, ee.apply, ee.Element.prototype.set, ee.Encodable.SourceFrame, ee.Feature, ee.Feature.prototype.getMapId, ee.Feature.prototype.getMap, ee.Feature.prototype.getInfo, ee.FeatureCollection.prototype.getMapId, ee.FeatureCollection.prototype.getDownloadURL, ee.FeatureCollection.prototype.select, ee.FeatureCollection.prototype.getInfo, ee.FeatureCollection, ee.FeatureCollection.prototype.getMap, ee.Filter.lt, ee.Filter.and, ee.Filter.or, ee.Filter.gte, ee.Filter.date, ee.Filter.inList, ee.Filter.prototype.not, + ee.ComputedObject.prototype.evaluate, ee.ComputedObject.prototype.aside, ee.ComputedObject.prototype.getInfo, ee.data.deleteAsset, ee.data.createAssetHome, ee.data.getList, ee.data.listAssets, ee.data.createAsset, ee.data.authenticateViaOauth, ee.data.getTableDownloadId, ee.data.getFeatureViewTilesKey, ee.data.getDownloadId, ee.data.makeDownloadUrl, ee.data.getInfo, ee.data.getAssetRootQuota, ee.data.computeValue, ee.data.listFeatures, ee.data.setAssetProperties, ee.data.getThumbId, ee.data.authenticateViaPopup, + ee.data.getAssetAcl, ee.data.getVideoThumbId, ee.data.getFilmstripThumbId, ee.data.makeThumbUrl, ee.data.resetWorkloadTag, ee.data.authenticateViaPrivateKey, ee.data.getAssetRoots, ee.data.listImages, ee.data.setAssetAcl, ee.data.listBuckets, ee.data.cancelOperation, ee.data.listOperations, ee.data.getTaskList, ee.data.cancelTask, ee.data.setDefaultWorkloadTag, ee.data.copyAsset, ee.data.updateAsset, ee.data.getTaskListWithLimit, ee.data.getAsset, ee.data.renameAsset, ee.data.newTaskId, ee.data.createFolder, + ee.data.makeTableDownloadUrl, ee.data.getTaskStatus, ee.data.authenticate, ee.data.getOperation, ee.data.getMapId, ee.data.startTableIngestion, ee.data.setWorkloadTag, ee.data.startProcessing, ee.data.updateTask, ee.data.getTileUrl, ee.data.getWorkloadTag, ee.data.startIngestion, ee.Date, ee.Deserializer.fromCloudApiJSON, ee.Deserializer.decodeCloudApi, ee.Deserializer.decode, ee.Deserializer.fromJSON, ee.Dictionary, ee.TILE_SIZE, ee.reset, ee.initialize, ee.call, ee.Algorithms, ee.InitState, ee.apply, + ee.Element.prototype.set, ee.Encodable.SourceFrame, ee.Feature, ee.Feature.prototype.getMapId, ee.Feature.prototype.getMap, ee.Feature.prototype.getInfo, ee.FeatureCollection.prototype.getMapId, ee.FeatureCollection.prototype.getDownloadURL, ee.FeatureCollection.prototype.select, ee.FeatureCollection.prototype.getInfo, ee.FeatureCollection, ee.FeatureCollection.prototype.getMap, ee.Filter.lt, ee.Filter.and, ee.Filter.or, ee.Filter.gte, ee.Filter.date, ee.Filter.inList, ee.Filter.prototype.not, ee.Filter, ee.Filter.metadata, ee.Filter.eq, ee.Filter.neq, ee.Filter.bounds, ee.Filter.gt, ee.Filter.lte, ee.Function.prototype.apply, ee.Function.prototype.call, ee.Geometry.Polygon, ee.Geometry.prototype.toGeoJSON, ee.Geometry.prototype.toGeoJSONString, ee.Geometry.BBox, ee.Geometry.LinearRing, ee.Geometry.Point, ee.Geometry.prototype.serialize, ee.Geometry.MultiPolygon, ee.Geometry, ee.Geometry.MultiPoint, ee.Geometry.MultiLineString, ee.Geometry.Rectangle, ee.Geometry.LineString, ee.Image.prototype.getMap, ee.Image.prototype.rename, ee.Image.prototype.getInfo, ee.Image.prototype.expression, ee.Image.prototype.getThumbId, ee.Image.rgb, ee.Image.prototype.getDownloadURL, ee.Image.prototype.getThumbURL, ee.Image.prototype.clip, ee.Image.prototype.select, ee.Image.cat, ee.Image, ee.Image.prototype.getMapId, ee.ImageCollection.prototype.getMapId, ee.ImageCollection.prototype.getMap, ee.ImageCollection.prototype.select, ee.ImageCollection.prototype.getInfo, ee.ImageCollection.prototype.linkCollection, ee.ImageCollection.prototype.first, ee.ImageCollection, ee.ImageCollection.prototype.getFilmstripThumbURL, ee.ImageCollection.prototype.getVideoThumbURL, ee.List, ee.Number, ee.Serializer.encodeCloudApi, ee.Serializer.toReadableJSON, ee.Serializer.toCloudApiJSON, ee.Serializer.toReadableCloudApiJSON, ee.Serializer.encode, ee.Serializer.toJSON, ee.Serializer.encodeCloudApiPretty, ee.String, ee.Terrain].forEach(function(fn, i) { diff --git a/javascript/package.json b/javascript/package.json index d4f29b5b6..fd8bd3a9d 100644 --- a/javascript/package.json +++ b/javascript/package.json @@ -1,6 +1,6 @@ { "name": "@google/earthengine", - "version": "0.1.381", + "version": "0.1.382", "description": "JavaScript client for Google Earth Engine API.", "author": "Google LLC", "license": "Apache-2.0", diff --git a/javascript/src/apiclient.js b/javascript/src/apiclient.js index 51f4832dd..f676ef447 100644 --- a/javascript/src/apiclient.js +++ b/javascript/src/apiclient.js @@ -25,7 +25,7 @@ const {trustedResourceUrl} = goog.require('safevalues'); /** @namespace */ const apiclient = {}; -const API_CLIENT_VERSION = '0.1.381'; +const API_CLIENT_VERSION = '0.1.382'; exports.VERSION = apiVersion.VERSION; exports.API_CLIENT_VERSION = API_CLIENT_VERSION; diff --git a/python/ee/__init__.py b/python/ee/__init__.py index 0dea524c0..1862db0a6 100644 --- a/python/ee/__init__.py +++ b/python/ee/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """The EE Python library.""" -__version__ = '0.1.381' +__version__ = '0.1.382' # Using lowercase function naming to match the JavaScript names. # pylint: disable=g-bad-name @@ -10,7 +10,7 @@ import datetime import inspect import os -from typing import Any, Hashable, List as ListType, Optional, Sequence, Type, Union +from typing import Any, Hashable, List as ListType, Optional, Sequence, Tuple, Type, Union from ee import _utils from ee import batch @@ -77,34 +77,37 @@ def __delattr__(self, name: Hashable) -> None: def Authenticate( authorization_code: Optional[str] = None, - quiet: bool = False, + quiet: Optional[bool] = None, code_verifier: Optional[str] = None, auth_mode: Optional[str] = None, scopes: Optional[Sequence[str]] = None, -) -> None: + force: bool = False, +) -> Optional[bool]: """Prompts the user to authorize access to Earth Engine via OAuth2. Args: authorization_code: An optional authorization code. quiet: If true, do not require interactive prompts and force --no-browser - mode for gcloud. + mode for gcloud-legacy. If false, never supply --no-browser. Default is + None, which autodetects the --no-browser setting. code_verifier: PKCE verifier to prevent auth code stealing. auth_mode: The authentication mode. One of: + "colab" - use the Colab authentication flow; "notebook" - send user to notebook authenticator page; - "gcloud" - use gcloud to obtain credentials (will set appdefault); - "appdefault" - read from existing $GOOGLE_APPLICATION_CREDENTIALS file; + "gcloud" - use gcloud to obtain credentials; + "gcloud-legacy" - use legacy gcloud flow to obtain credentials; "localhost" - runs auth flow in local browser only; None - a default mode is chosen based on your environment. - scopes: List of scopes to use for authentication. Defaults to [ - 'https://www.googleapis.com/auth/earthengine', - 'https://www.googleapis.com/auth/devstorage.full_control' ]. + scopes: List of scopes to use for authentication. Defaults to [ + 'https://www.googleapis.com/auth/earthengine', + 'https://www.googleapis.com/auth/devstorage.full_control' ]. + force: Will force authentication even if valid credentials already exist. Returns: - (auth_url, code_verifier) when called with quiet='init_only' + True if we found valid credentials and didn't run the auth flow. """ - oauth.authenticate( - authorization_code, quiet, code_verifier, auth_mode, scopes - ) + return oauth.authenticate(authorization_code, quiet, code_verifier, auth_mode, + scopes, force) @_utils.accept_opt_prefix('opt_url') diff --git a/python/ee/cli/commands.py b/python/ee/cli/commands.py index fcb1bb972..0e7f76fd1 100644 --- a/python/ee/cli/commands.py +++ b/python/ee/cli/commands.py @@ -380,17 +380,21 @@ def __init__(self, parser: argparse.ArgumentParser): parser.add_argument( '--quiet', action='store_true', - help='Do not prompt for input, and run gcloud in no-browser mode.') + help='Do not prompt for input, run gcloud-legacy in no-browser mode.') parser.add_argument( '--code-verifier', help='PKCE verifier to prevent auth code stealing.') parser.add_argument( '--auth_mode', - help='One of: notebook - use notebook authenticator; gcloud - use' - ' gcloud; appdefault - read GOOGLE_APPLICATION_CREDENTIALS;' - ' localhost[:PORT] - use local browser') + help='One of: notebook - use notebook authenticator; colab - use Colab' + ' authenticator; gcloud / gcloud-legacy - use gcloud;' + ' localhost[:PORT|:0] - use local browser') parser.add_argument( '--scopes', help='Optional comma-separated list of scopes.') + parser.add_argument( + '--force', + action='store_true', + help='Run authentication even if credentials already exist.') def run( self, args: argparse.Namespace, config: utils.CommandLineConfig @@ -400,10 +404,11 @@ def run( # Filter for arguments relevant for ee.Authenticate() args_auth = {x: vars(args)[x] for x in ( - 'authorization_code', 'quiet', 'code_verifier', 'auth_mode')} + 'authorization_code', 'quiet', 'code_verifier', 'auth_mode', 'force')} if args.scopes: args_auth['scopes'] = args.scopes.split(',') - ee.Authenticate(**args_auth) + if ee.Authenticate(**args_auth): + print('Authenticate: Credentials already exist. Use --force to refresh.') class SetProjectCommand: @@ -420,11 +425,17 @@ def run( """Saves the project to the config file.""" config_path = config.config_file - with open(config_path) as config_file_json: - config = json.load(config_file_json) + try: + with open(config_path) as json_config_file: + config = json.load(json_config_file) + except FileNotFoundError: + # File may not exist if we initialized from default credentials. + config = {} config['project'] = args.project - json.dump(config, open(config_path, 'w')) + # Existing file permissions will be left unchanged if file already exists. + with open(config_path, 'w') as json_config_file: + json.dump(config, json_config_file) print('Successfully saved project id') @@ -443,12 +454,18 @@ def run( del args # Unused. config_path = config.config_file - with open(config_path) as config_file_json: - config = json.load(config_file_json) + try: + with open(config_path) as json_config_file: + config = json.load(json_config_file) + except FileNotFoundError: + # File may not exist if we initialized from default credentials. + config = {} if 'project' in config: del config['project'] - json.dump(config, open(config_path, 'w')) + # Existing file permissions will be left unchanged if file already exists. + with open(config_path, 'w') as json_config_file: + json.dump(config, json_config_file) print('Successfully unset project id') diff --git a/python/ee/data.py b/python/ee/data.py index f6d4e4248..92da66e3c 100644 --- a/python/ee/data.py +++ b/python/ee/data.py @@ -13,6 +13,7 @@ from typing import Any, Callable, Dict, Iterator, List, Optional, Sequence, Union import uuid +import google.auth # Rename to avoid redefined-outer-name warning. from google.oauth2 import credentials as credentials_lib import google_auth_httplib2 @@ -245,10 +246,16 @@ def get_persistent_credentials() -> credentials_lib.Credentials: None, **oauth.get_credentials_arguments() ) except IOError: + # Before raising, try default credentials as a fallback. + try: + credentials, unused_project_id = google.auth.default() + return credentials + except google.auth.exceptions.DefaultCredentialsError: + pass raise ee_exception.EEException( # pylint: disable=raise-missing-from 'Please authorize access to your Earth Engine account by ' 'running\n\nearthengine authenticate\n\n' - 'in your command line, and then retry.' + 'in your command line, or ee.Authenticate() in Python, and then retry.' ) diff --git a/python/ee/oauth.py b/python/ee/oauth.py index 3fb6e5822..8d024882c 100644 --- a/python/ee/oauth.py +++ b/python/ee/oauth.py @@ -17,13 +17,20 @@ import os import shutil import subprocess +import sys from typing import Any, Dict, Optional, Sequence, Union import urllib.error import urllib.parse import urllib.request import webbrowser +import google.auth from google.auth import _cloud_sdk +import google.auth.transport.requests + +from ee import data as ee_data +from ee.ee_exception import EEException + # Optional imports used for specific shells. # pylint: disable=g-import-not-at-top @@ -59,6 +66,8 @@ # Command-line browsers cannot handle the auth pages. TEXT_BROWSERS = ['elinks', 'links', 'lynx', 'w3m', 'www-browser'] +# Environment variables indicating valid compositors on Linux. +DISPLAY_VARIABLES = ['DISPLAY', 'WAYLAND_DISPLAY', 'MIR_SOCKET'] def get_credentials_path() -> str: @@ -80,6 +89,15 @@ def get_credentials_arguments() -> Dict[str, Any]: return args +def _valid_credentials_exist() -> bool: + try: + creds = ee_data.get_persistent_credentials() + creds.refresh(google.auth.transport.requests.Request()) + return True + except (EEException, google.auth.exceptions.RefreshError): + return False + + def get_authorization_url( code_challenge: str, scopes: Optional[Sequence[str]] = None, @@ -152,7 +170,7 @@ def write_private_json(json_path: str, info_dict: Dict[str, Any]) -> None: def _in_colab_shell() -> bool: """Tests if the code is being executed within Google Colab.""" try: - import google.colab # pylint: disable=unused-import + import google.colab # pylint: disable=unused-import,redefined-outer-name return True except ImportError: return False @@ -194,6 +212,8 @@ def _obtain_and_write_token( fetch_client = urllib.request.Request(FETCH_URL, data=data, headers=headers) fetched_info = json.loads( urllib.request.urlopen(fetch_client).read().decode()) + if 'error' in fetched_info: + raise EEException('Cannot authenticate: %s' % fetched_info['error']) client_info = {k: fetched_info[k] for k in ['client_id', 'client_secret']} scopes = fetched_info.get('scopes') or scopes token = request_token(auth_code.strip(), code_verifier, **client_info) @@ -274,29 +294,40 @@ def _nonce_table(*nonce_keys: str) -> Dict[str, str]: return {k: v.decode() for k, v in table.items()} -def _open_new_browser(url: str) -> None: - """Opens a web browser if possible.""" +def _open_new_browser(url: str) -> bool: + """Opens a web browser if possible, returning True when so.""" try: browser = webbrowser.get() if hasattr(browser, 'name') and browser.name in TEXT_BROWSERS: - return + return False except webbrowser.Error: - return - webbrowser.open_new(url) + return False + if url: + webbrowser.open_new(url) + return True + + +def _localhost_is_viable() -> bool: + valid_display = 'linux' not in sys.platform or any( + os.environ.get(var) for var in DISPLAY_VARIABLES) + return valid_display and _open_new_browser('') -def _in_notebook() -> bool: - return _in_colab_shell() or _in_jupyter_shell() +def _no_gcloud() -> bool: + return not shutil.which(GCLOUD_COMMAND.split()[0]) -def _load_app_default_credentials( - run_gcloud: bool = True, +def _load_gcloud_credentials( scopes: Optional[Sequence[str]] = None, quiet: Optional[bool] = None, + run_gcloud_legacy: bool = False, ) -> None: - """Initializes credentials from ADC, optionally running gcloud to get them.""" - adc_path = _cloud_sdk.get_application_default_credentials_path() - if run_gcloud: + """Initializes credentials by running gcloud flows.""" + client_id_file = None + command = GCLOUD_COMMAND.split() + command[0] = shutil.which(command[0]) or command[0] # Windows fix + command += ['--scopes=%s' % (','.join(scopes or SCOPES))] + if run_gcloud_legacy: client_id_json = dict( client_id=CLIENT_ID, client_secret=CLIENT_SECRET, @@ -305,32 +336,28 @@ def _load_app_default_credentials( token_uri=TOKEN_URI) client_id_file = get_credentials_path() + '-client-id.json' write_private_json(client_id_file, dict(installed=client_id_json)) - command = GCLOUD_COMMAND.split() - command[0] = shutil.which(command[0]) or command[0] # Windows fix - command += ['--scopes=%s' % (','.join(scopes or SCOPES))] command += ['--client-id-file=%s' % client_id_file] - command += ['--no-browser'] if quiet else [] - print('Fetching credentials using gcloud') - more_info = '\nMore information: ' + ( - 'https://developers.google.com/earth-engine/guides/python_install\n') - try: - subprocess.run(command, check=True) - except FileNotFoundError as e: - tip = 'Please ensure that gcloud is installed.' + more_info - raise Exception('gcloud command not found. ' + tip) from e # pylint:disable=broad-exception-raised - except subprocess.CalledProcessError as e: - tip = ('Please check for any errors above.\n*Possible fixes:' - ' If you loaded a page with a "redirect_uri_mismatch" error,' - ' run earthengine authenticate with the --quiet flag;' - ' if the error page says "invalid_request", be sure to run the' - ' entire gcloud auth command that is shown.' + more_info) - raise Exception('gcloud failed. ' + tip) from e # pylint:disable=broad-exception-raised - finally: + force_quiet = quiet is None and not _localhost_is_viable() + command += ['--no-browser'] if quiet or force_quiet else [] + print('Fetching credentials using gcloud') + more_info = '\nMore information: ' + ( + 'https://developers.google.com/earth-engine/guides/auth\n') + try: + subprocess.run(command, check=True) + except FileNotFoundError as e: + tip = 'Please ensure that gcloud is installed.' + more_info + raise Exception('gcloud command not found. ' + tip) from e # pylint:disable=broad-exception-raised + except subprocess.CalledProcessError as e: + tip = ('Please check for any errors above.\n*Possible fixes:' + ' If you loaded a page with a "redirect_uri_mismatch" error,' + ' run earthengine authenticate with the --quiet flag;' + ' if the error page says "invalid_request", be sure to run the' + ' entire gcloud auth command that is shown.' + more_info) + raise Exception('gcloud failed. ' + tip) from e # pylint:disable=broad-exception-raised + finally: + if client_id_file: os.remove(client_id_file) - else: - # Only consult the environment variable in appdefault mode, because gcloud - # always writes to the default location. - adc_path = os.getenv('GOOGLE_APPLICATION_CREDENTIALS', adc_path) + adc_path = _cloud_sdk.get_application_default_credentials_path() with open(adc_path) as adc_json: adc = json.load(adc_json) adc = {k: adc[k] for k in ['client_id', 'client_secret', 'refresh_token']} @@ -379,33 +406,42 @@ def fetch_code(self) -> Optional[str]: def authenticate( cli_authorization_code: Optional[str] = None, - quiet: bool = False, + quiet: Optional[bool] = None, cli_code_verifier: Optional[str] = None, auth_mode: Optional[str] = None, scopes: Optional[Sequence[str]] = None, -) -> None: + force: bool = False, +) -> bool: """Prompts the user to authorize access to Earth Engine via OAuth2. Args: cli_authorization_code: An optional authorization code. Supports CLI mode, where the code is passed as an argument to `earthengine authenticate`. - quiet: If true, do not require interactive prompts. + quiet: If true, do not require interactive prompts and force --no-browser + mode for gcloud-legacy. If false, never supply --no-browser. Default is + None, which autodetects the --no-browser setting. cli_code_verifier: PKCE verifier to prevent auth code stealing. Must be provided if cli_authorization_code is given. auth_mode: The authorization mode. One of: + "colab" - use the Colab authentication flow. "notebook" - send user to notebook authenticator page. Intended for web users who do not run code locally. Credentials expire in 7 days. - "gcloud" - use gcloud to obtain credentials. This runs a command line to - set the appdefault file, which must run on your local machine. - "appdefault" - read an existing $GOOGLE_APPLICATION_CREDENTIALS file - without running gcloud. + "gcloud" - use gcloud to obtain credentials. "localhost" - sends credentials to the Python environment on the same localhost as the browser. Does not work for remote shells. Default port is 8085; use localhost:N set port or localhost:0 to auto-select. + "gcloud-legacy" - use less convenient gcloud mode, for users without + cloud projects. + "appdefault" - included for legacy compatibility but not necessary. + ee.Initialize() will always check for application default credentials. None - a default mode is chosen based on your environment. scopes: List of scopes to use for authorization. Defaults to [ 'https://www.googleapis.com/auth/earthengine', 'https://www.googleapis.com/auth/devstorage.full_control' ]. + force: Will force authentication even if valid credentials already exist. + + Returns: + True if we found valid credentials and didn't run the auth flow. Raises: Exception: on invalid arguments. @@ -415,11 +451,31 @@ def authenticate( _obtain_and_write_token(cli_authorization_code, cli_code_verifier, scopes) return + if not force and _valid_credentials_exist(): + return True + if not auth_mode: - auth_mode = 'notebook' if _in_notebook() else 'gcloud' + if _in_colab_shell(): + auth_mode = 'colab' + elif _in_jupyter_shell(): + auth_mode = 'notebook' + elif _localhost_is_viable() and _no_gcloud(): + auth_mode = 'localhost' + else: + auth_mode = 'gcloud' + + if auth_mode in ['gcloud', 'gcloud-legacy']: + _load_gcloud_credentials(scopes, quiet, auth_mode == 'gcloud-legacy') + return + + if auth_mode == 'colab': + from google.colab import auth # pylint: disable=g-import-not-at-top # pytype: disable=import-error + auth.authenticate_user() + return - if auth_mode in ['appdefault', 'gcloud']: - _load_app_default_credentials(auth_mode == 'gcloud', scopes, quiet) + if auth_mode == 'appdefault': + print('appdefault no longer necessary: ee.Initialize() always checks ADC', + file=sys.stderr) return flow = Flow(auth_mode, scopes) @@ -468,7 +524,7 @@ def __init__( scopes=urllib.parse.quote(' '.join(self.scopes)), **request_info) self.code_verifier = ':'.join(request_info[k] for k in nonces) else: - raise Exception('Unknown auth_mode "%s"' % auth_mode) # pylint:disable=broad-exception-raised + raise EEException('Unknown auth_mode "%s"' % auth_mode) # pylint:disable=broad-exception-raised def save_code(self, code: Optional[str] = None) -> None: """Fetches auth code if not given, and saves the generated credentials.""" diff --git a/python/ee/tests/algorithms.json b/python/ee/tests/algorithms.json index 37e5922c6..e3db34eb8 100644 --- a/python/ee/tests/algorithms.json +++ b/python/ee/tests/algorithms.json @@ -3151,6 +3151,15 @@ "optional": true, "defaultValue": null }] + }, { + "name": "algorithms/Classifier.load", + "description": "Creates a Classifier.", + "returnType": "Classifier", + "arguments": [{ + "argumentName": "id", + "type": "String", + "description": "The Classifier\u0027s Asset ID." + }] }, { "name": "algorithms/Classifier.mode", "description": "Returns the classifier mode: `CLASSIFICATION`, `REGRESSION`, `PROBABILITY`, `MULTIPROBABILITY`, `RAW` or `RAW_REGRESSION`.", diff --git a/python/pyproject.toml b/python/pyproject.toml index 74e64449f..9db5ce55d 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "earthengine-api" -version = "0.1.381" +version = "0.1.382" description = "Earth Engine Python API" requires-python = ">=3.7" keywords = [