Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
edgekv auto token renewal changes
  • Loading branch information
Shushma44 authored Aug 2, 2024
1 parent d1cee66 commit 7ac9830
Show file tree
Hide file tree
Showing 3 changed files with 393 additions and 0 deletions.
10 changes: 10 additions & 0 deletions public_examples/edgekv-tokens.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@


var edgekv_access_tokens = {
"namespace-test": {
"name": "my_auto_token_07252024",
"reference": "9e427ea3-33f4-5430-8794-22f1c21b1378",
}
};
export { edgekv_access_tokens };

346 changes: 346 additions & 0 deletions public_examples/edgekv.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,346 @@
/*
(c) Copyright 2020 Akamai Technologies, Inc. Licensed under Apache 2 license.
Version: 0.6.2
Purpose: Provide a helper class to simplify the interaction with EdgeKV in an EdgeWorker.
Repo: https://github.com/akamai/edgeworkers-examples/tree/master/edgekv/lib
*/

import { TextDecoderStream } from 'text-encode-transform';
import { WritableStream } from 'streams';
import { httpRequest } from 'http-request';
/**
* You must include edgekv_tokens.js in your bundle for this class to function properly.
* edgekv_tokens.js must include all namespaces you are going to use in the bundle.
*/
import { edgekv_access_tokens } from './edgekv_tokens.js';

export class EdgeKV {
#namespace;
#group;
#edgekv_uri;
#token_override;
#num_retries_on_timeout;
#sandbox_id;
#sandbox_fallback;

/**
* Constructor to allow setting default namespace and group
* These defaults can be overriden when making individual GET, PUT, and DELETE operations
* @param {string} [$0.namepsace="default"] the default namespace to use for all GET, PUT, and DELETE operations
* Namespace must be 32 characters or less, consisting of A-Z a-z 0-9 _ or -
* @param {string} [$0.group="default"] the default group to use for all GET, PUT, and DELETE operations
* Group must be 128 characters or less, consisting of A-Z a-z 0-9 _ or -
* @param {number} [$0.num_retries_on_timeout=0] the number of times to retry a GET requests when the sub request times out
* @param {object} [$0.ew_request=null] passes the request object from the EdgeWorkers event handler to enable access to EdgeKV data in sandbox environments
* @param {boolean} [$0.sandbox_fallback=false] whether to fallback to retrieving staging data if the sandbox data does not exist, instead of returning null or the specified default value
*/
constructor(namespace = "default", group = "default") {
if (typeof namespace === "object") {
this.#namespace = namespace.namespace || "default";
this.#group = namespace.group || "default";
this.#edgekv_uri = namespace.edgekv_uri || "https://edgekv.akamai-edge-svcs.net";
this.#token_override = namespace.token_override || null;
this.#num_retries_on_timeout = namespace.num_retries_on_timeout || 0;
this.#sandbox_id = (namespace.ew_request ? (namespace.ew_request.sandboxId || null) : null);
this.#sandbox_fallback = namespace.sandbox_fallback || false;
} else {
this.#namespace = namespace;
this.#group = group;
this.#edgekv_uri = "https://akab-2qyg6wxni4xgsfmr-cbg4lshd3gcnvwp5.luna.akamaiapis.net";
this.#token_override = null;
this.#num_retries_on_timeout = 0;
this.#sandbox_id = null;
this.#sandbox_fallback = false;
}
}

throwError(failed_reason, status, body) {
throw {
failed: failed_reason,
status: status,
body: body,
toString: function () { return JSON.stringify(this); }
};
}

async requestHandlerTemplate(http_request, handler_200, handler_large_200, error_text, default_value, num_retries_on_timeout) {
try {
let response = await http_request();
switch (response.status) {
case 200:
// need to handle content length > 128000 bytes differently in EdgeWorkers
let contentLength = response.getHeader('Content-Length');
if (!contentLength || contentLength.length == 0 || contentLength[0] >= 128000) {
return handler_large_200(response);
} else {
return handler_200(response);
}
case 404:
return default_value;
default:
let content = "";
try {
content = await response.text();
content = JSON.parse(content);
} catch (error) { }
throw { status: response.status, body: content }; // to be caught in surrounding catch block
}
} catch (error) {
if (num_retries_on_timeout > 0 && /^.*subrequest to URL.*timed out.*$/.test(error.toString())) {
return this.requestHandlerTemplate(http_request, handler_200, handler_large_200, error_text, default_value, num_retries_on_timeout - 1);
}
if (error.hasOwnProperty('status')) {
this.throwError(error_text + " FAILED", error.status, error.body);
}
this.throwError(error_text + " FAILED", 0, error.toString());
}
}

validate({ namespace = null, group = null, item = null }) {
if (!namespace || !/^[A-Za-z0-9_-]{1,32}$/.test(namespace)) {
throw "Namespace is not valid. Must be 32 characters or less, consisting of A-Z a-z 0-9 _ or -";
}
if (!group || !/^[A-Za-z0-9_-]{1,128}$/.test(group)) {
throw "Group is not valid. Must be 128 characters or less, consisting of A-Z a-z 0-9 _ or -";
}
if (!item || !/^[A-Za-z0-9_-]{1,512}$/.test(item)) {
throw "Item is not valid. Must be 512 characters or less, consisting of A-Z a-z 0-9 _ or -";
}
}

getNamespaceToken(namespace) {
if (this.#token_override) {
return this.#token_override;
}
let name = "namespace-" + namespace;
if (!(name in edgekv_access_tokens)) {
throw "MISSING ACCESS TOKEN. No EdgeKV Access Token defined for namespace '" + namespace + "'.";
}
return edgekv_access_tokens[name]["reference"];
}

addTimeout(options, timeout) {
if (timeout && (typeof timeout !== 'number' || !isFinite(timeout) || timeout <= 0 || timeout > 1000)) {
throw "Timeout is not valid. Must be a number greater than 0 and less than 1000.";
}
if (timeout) {
options.timeout = timeout;
}
return options;
}

addSandboxId(uri) {
if (this.#sandbox_id) {
uri = uri + "?sandboxId=" + this.#sandbox_id;
if (this.#sandbox_fallback) {
uri = uri + "&sandboxFallback=true";
}
}
return uri;
}

async streamText(response_body) {
let result = "";
await response_body
.pipeThrough(new TextDecoderStream())
.pipeTo(new WritableStream({
write(chunk) {
result += chunk;
}
}), { preventAbort: true });
return result;
}

async streamJson(response_body) {
return JSON.parse(await this.streamText(response_body));
}

putRequest({ namespace = this.#namespace, group = this.#group, item, value, timeout = null } = {}) {
this.validate({ namespace: namespace, group: group, item: item });
let uri = this.#edgekv_uri + "/api/v1/namespaces/" + namespace + "/groups/" + group + "/items/" + item;
return httpRequest(this.addSandboxId(uri), this.addTimeout({
method: "PUT",
body: typeof value === "object" ? JSON.stringify(value) : value,
headers: { "X-Akamai-EdgeDB-Auth": [this.getNamespaceToken(namespace)] }
}, timeout));
}

/**
* async PUT text into an item in the EdgeKV.
* @param {string} [$0.namepsace=this.#namespace] specify a namespace other than the default
* @param {string} [$0.group=this.#group] specify a group other than the default
* @param {string} $0.item item key to put into the EdgeKV
* @param {string} $0.value text value to put into the EdgeKV
* @param {number} [$0.timeout=null] the maximum time, between 1 and 1000 milliseconds, to wait for the response
* @returns {Promise<string>} if the operation was successful, the response from the EdgeKV
* @throws {object} if the operation was not successful,
* an object describing the non-200 response from the EdgeKV: {failed, status, body}
*/
async putText({ namespace = this.#namespace, group = this.#group, item, value, timeout = null } = {}) {
return this.requestHandlerTemplate(
() => this.putRequest({ namespace: namespace, group: group, item: item, value: value, timeout: timeout }),
(response) => response.text(),
(response) => this.streamText(response.body),
"PUT",
null,
0
);
}

/**
* PUT text into an item in the EdgeKV while only waiting for the request to send and not for the response.
* @param {string} [$0.namepsace=this.#namespace] specify a namespace other than the default
* @param {string} [$0.group=this.#group] specify a group other than the default
* @param {string} $0.item item key to put into the EdgeKV
* @param {string} $0.value text value to put into the EdgeKV
* @throws {object} if the operation was not successful at sending the request,
* an object describing the error: {failed, status, body}
*/
putTextNoWait({ namespace = this.#namespace, group = this.#group, item, value } = {}) {
try {
this.putRequest({ namespace: namespace, group: group, item: item, value: value });
} catch (error) {
this.throwError("PUT FAILED", 0, error.toString());
}
}

/**
* async PUT json into an item in the EdgeKV.
* @param {string} [$0.namepsace=this.#namespace] specify a namespace other than the default
* @param {string} [$0.group=this.#group] specify a group other than the default
* @param {string} $0.item item key to put into the EdgeKV
* @param {object} $0.value json value to put into the EdgeKV
* @param {number} [$0.timeout=null] the maximum time, between 1 and 1000 milliseconds, to wait for the response
* @returns {Promise<string>} if the operation was successful, the response from the EdgeKV
* @throws {object} if the operation was not successful,
* an object describing the non-200 response from the EdgeKV: {failed, status, body}
*/
async putJson({ namespace = this.#namespace, group = this.#group, item, value, timeout = null } = {}) {
return this.requestHandlerTemplate(
() => this.putRequest({ namespace: namespace, group: group, item: item, value: JSON.stringify(value), timeout: timeout }),
(response) => response.text(),
(response) => this.streamText(response.body),
"PUT",
null,
0
);
}

/**
* PUT json into an item in the EdgeKV while only waiting for the request to send and not for the response.
* @param {string} [$0.namepsace=this.#namespace] specify a namespace other than the default
* @param {string} [$0.group=this.#group] specify a group other than the default
* @param {string} $0.item item key to put into the EdgeKV
* @param {object} $0.value json value to put into the EdgeKV
* @throws {object} if the operation was not successful at sending the request,
* an object describing the error: {failed, status, body}
*/
putJsonNoWait({ namespace = this.#namespace, group = this.#group, item, value } = {}) {
try {
this.putRequest({ namespace: namespace, group: group, item: item, value: JSON.stringify(value) });
} catch (error) {
this.throwError("PUT FAILED", 0, error.toString());
}
}

getRequest({ namespace = this.#namespace, group = this.#group, item, timeout = null } = {}) {
this.validate({ namespace: namespace, group: group, item: item });
let uri = this.#edgekv_uri + "/api/v1/namespaces/" + namespace + "/groups/" + group + "/items/" + item;
return httpRequest(this.addSandboxId(uri), this.addTimeout({
method: "GET",
headers: { "X-Akamai-EdgeDB-Auth": [this.getNamespaceToken(namespace)] }
}, timeout));
}

/**
* async GET text from an item in the EdgeKV.
* @param {string} [$0.namepsace=this.#namespace] specify a namespace other than the default
* @param {string} [$0.group=this.#group] specify a group other than the default
* @param {string} $0.item item key to get from the EdgeKV
* @param {string} [$0.default_value=null] the default value to return if a 404 response is returned from EdgeKV
* @param {number} [$0.timeout=null] the maximum time, between 1 and 1000 milliseconds, to wait for the response
* @param {number} [$0.num_retries_on_timeout=null] the number of times to retry a requests when the sub request times out
* @returns {Promise<string>} if the operation was successful, the text response from the EdgeKV or the default_value on 404
* @throws {object} if the operation was not successful,
* an object describing the non-200 and non-404 response from the EdgeKV: {failed, status, body}
*/
async getText({ namespace = this.#namespace, group = this.#group, item, default_value = null, timeout = null, num_retries_on_timeout = null } = {}) {
return this.requestHandlerTemplate(
() => this.getRequest({ namespace: namespace, group: group, item: item, timeout: timeout }),
(response) => response.text(),
(response) => this.streamText(response.body),
"GET TEXT",
default_value,
num_retries_on_timeout ?? this.#num_retries_on_timeout
);
}

/**
* async GET json from an item in the EdgeKV.
* @param {string} [$0.namepsace=this.#namespace] specify a namespace other than the default
* @param {string} [$0.group=this.#group] specify a group other than the default
* @param {string} $0.item item key to get from the EdgeKV
* @param {object} [$0.default_value=null] the default value to return if a 404 response is returned from EdgeKV
* @param {number} [$0.timeout=null] the maximum time, between 1 and 1000 milliseconds, to wait for the response
* @param {number} [$0.num_retries_on_timeout=null] the number of times to retry a requests when the sub request times out
* @returns {Promise<object>} if the operation was successful, the json response from the EdgeKV or the default_value on 404
* @throws {object} if the operation was not successful,
* an object describing the non-200 and non-404 response from the EdgeKV: {failed, status, body}
*/
async getJson({ namespace = this.#namespace, group = this.#group, item, default_value = null, timeout = null, num_retries_on_timeout = null } = {}) {
return this.requestHandlerTemplate(
() => this.getRequest({ namespace: namespace, group: group, item: item, timeout: timeout }),
(response) => response.json(),
(response) => this.streamJson(response.body),
"GET JSON",
default_value,
num_retries_on_timeout ?? this.#num_retries_on_timeout
);
}

deleteRequest({ namespace = this.#namespace, group = this.#group, item, timeout = null } = {}) {
this.validate({ namespace: namespace, group: group, item: item });
let uri = this.#edgekv_uri + "/api/v1/namespaces/" + namespace + "/groups/" + group + "/items/" + item;
return httpRequest(this.addSandboxId(uri), this.addTimeout({
method: "DELETE",
headers: { "X-Akamai-EdgeDB-Auth": [this.getNamespaceToken(namespace)] }
}, timeout));
}

/**
* async DELETE an item in the EdgeKV.
* @param {string} [$0.namepsace=this.#namespace] specify a namespace other than the default
* @param {string} [$0.group=this.#group] specify a group other than the default
* @param {string} $0.item item key to delete from the EdgeKV
* @param {number} [$0.timeout=null] the maximum time, between 1 and 1000 milliseconds, to wait for the response
* @returns {Promise<string>} if the operation was successful, the text response from the EdgeKV
* @throws {object} if the operation was not successful,
* an object describing the non-200 response from the EdgeKV: {failed, status, body}
*/
async delete({ namespace = this.#namespace, group = this.#group, item, timeout = null} = {}) {
return this.requestHandlerTemplate(
() => this.deleteRequest({ namespace: namespace, group: group, item: item, timeout: timeout }),
(response) => response.text(),
(response) => this.streamText(response.body),
"DELETE",
null,
0
);
}

/**
* DELETE an item in the EdgeKV while only waiting for the request to send and not for the response.
* @param {string} [$0.namepsace=this.#namespace] specify a namespace other than the default
* @param {string} [$0.group=this.#group] specify a group other than the default
* @param {string} $0.item item key to delete from the EdgeKV
* @throws {object} if the operation was not successful at sending the request,
* an object describing the error: {failed, status, body}
*/
deleteNoWait({ namespace = this.#namespace, group = this.#group, item } = {}) {
try {
this.delete({ namespace: namespace, group: group, item: item });
} catch (error) {
this.throwError("DELETE FAILED", 0, error.toString());
}
}
}
37 changes: 37 additions & 0 deletions public_examples/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
(c) Copyright 2019 Akamai Technologies, Inc. Licensed under Apache 2 license.
Version: 0.1
Purpose: Respond with empty JSON if cart cookie not part of request.
*/
import { EdgeKV } from "./edgekv.js";
import { Rule, defaultEngine, Operator } from "js-rules-engine";
import { logger } from "log";

init();


function init() {
const caseInsensitivelyEquals = new Operator(
"caseInsensitivelyEquals",
function (a, b) {
return a.toUpperCase() === b.toUpperCase();
}
);

defaultEngine.addOperator(caseInsensitivelyEquals);

}


export async function responseProvider(request)
{
//logger.log(`In responseProvider function request - ${JSON.stringify(decodeURI(request))}`);
try
{
//let dccFeatureObj = JSON.parse(request.getVariable("PMUSER_DCC_CONFIG"));
const edgeKv = new EdgeKV({ namespace: "edgekv-token-test", group: "1111" });
logger.log('responseProvider function edgeKv: ', edgeKv);
}catch(error){
logger.log('responseProvider function error: ', error);
}
}

0 comments on commit 7ac9830

Please sign in to comment.