Created
May 5, 2021 15:30
-
-
Save tbrannam/cdad27d58a05bce2eed5d692f8af89ef to your computer and use it in GitHub Desktop.
experiment assembly script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { URL } from "@fastly/as-url"; | |
import { RegExp } from "assemblyscript-regex"; | |
import { Dictionary } from "@fastly/as-compute" | |
import { JSON } from "assemblyscript-json"; | |
// import { md5 } from "hash-wasm"; | |
function normalizePath(url: URL): URL { | |
const filenameRegEx = new RegExp(".*\.([a-zA-Z0-9]{1,11})$", "g"); | |
let path = url.pathname; | |
// if url doesn't have a filename extension, then add / and/or index.html | |
const match = filenameRegEx.exec(path); | |
if (match == null) { | |
if (path.endsWith("/") == false) { | |
// url.pathname = path + "/index.html" | |
path = path + "/index.html"; | |
} else { | |
path = path + "index.html" | |
} | |
const result = new URL(url.toString()); | |
return result | |
} | |
} | |
class MockDictionary extends Dictionary { | |
constructor(name: string, private mockValues: Map<string, string | null>) { | |
super(name) | |
} | |
get(key: string): string | null { | |
if (this.mockValues.has(key)) { | |
return this.mockValues.get(key); | |
} | |
return null; | |
} | |
contains(key: string): boolean { | |
const strOrNull = this.mockValues.has(key); | |
return (strOrNull != null) | |
} | |
} | |
function versionPath(currentVersionDictionary: Dictionary, publishedVersionsDictionary: Dictionary): string | null { | |
const versionKey: string | null = currentVersionDictionary.get("default"); | |
if (versionKey) { | |
return publishedVersionsDictionary.get(versionKey); | |
} | |
return null; | |
} | |
function getExperimentPrefix(versionHash: string, experimentsDictionary: Dictionary): string { | |
// the format of the experimentsDictionary is convoluted, but worked around VCL limitations (no loops) | |
// we will need to revisit this format to something more appropriate for the language | |
// ideas were based around Fastly AB Test example https://developer.fastly.com/solutions/examples/a/b-testing | |
const testKey = versionHash + "-tests"; | |
const testsValue = <string | null>experimentsDictionary.get(testKey); | |
if (!testsValue) { | |
return ''; | |
} | |
if (testsValue == "default") { | |
return <string>testsValue; | |
} | |
const tests = (testsValue as string).split(',').map<string>((val: string) => val.trim()); | |
let acc = '' | |
for (let ii = 0; ii < tests.length; ii++) { | |
const current = tests[ii]; | |
const currentKey = versionHash + "-test-" + current; | |
hash(currentKey); | |
// log(currentKey); | |
const currentExperiment = <string>experimentsDictionary.get(currentKey); | |
const pick = seededRandomRange(0, currentExperiment.length, 1301); | |
acc = acc + currentExperiment.charAt(pick); | |
} | |
// log(acc); | |
return acc; | |
} | |
function seededRandomRange(from: i32, to: i32, seed: i64): i32 { | |
NativeMath.seedRandom(seed); | |
return <i32>NativeMath.floor((to - from) * NativeMath.random()) + from | |
} | |
// function hashCode(str: string): number { | |
// return str.split('').reduce((prevHash, currVal) => | |
// (((prevHash << 5) - prevHash) + currVal.charCodeAt(0)) | 0, 0); | |
// } | |
const CURRENT_VERSION_HASH = "0d66e7b9ac83c881"; | |
describe("normalizePath", () => { | |
it("do the thing", () => { | |
expect(seededRandomRange(1, 10, 123)).toBe(6); | |
md5("foo"); | |
}) | |
// it("should read the current version path", () => { | |
// const currentVersionHash = currentVersionDictionary.get('default'); | |
// expect(currentVersionHash).toBe(CURRENT_VERSION_HASH); | |
// }) | |
it("returns versioned prefix", () => { | |
const path = "/home/index.html" | |
const CURRENT_VERSION_HASH = "0d66e7b9ac83c881"; | |
const versionMap = new Map<string, string | null>(); | |
versionMap.set('default', CURRENT_VERSION_HASH); | |
const currentVersionDictionary = new MockDictionary('current_version', versionMap); | |
const publishedVersionMap = new Map<string, string | null>(); | |
publishedVersionMap.set('0d66e7b9ac83c881', '20210322_1203.59329'); | |
const publishedVersionsDictionary = new MockDictionary('published_versions', publishedVersionMap); | |
const experimentMap = new Map<string, string | null>(); | |
experimentMap.set('0d66e7b9ac83c881-tests', 'cartlocation, Checkout_Card_With_Name_Field'); | |
experimentMap.set('0d66e7b9ac83c881-test-cartlocation', 'AAAAAAAAAB'); | |
experimentMap.set('0d66e7b9ac83c881-test-Checkout_Card_With_Name_Field', 'AB'); | |
const experimentsDictionary = new MockDictionary('experiments', experimentMap); | |
const currentVersionHash = currentVersionDictionary.get('default'); | |
if (currentVersionHash) { | |
log('doit'); | |
const publishedVersionPrefix = versionPath(currentVersionDictionary, publishedVersionsDictionary); | |
expect(publishedVersionPrefix).toBe("20210322_1203.59329"); | |
const experimentPrefix = getExperimentPrefix(currentVersionHash, experimentsDictionary); | |
log(experimentPrefix); | |
const s3Path = [ | |
null, | |
null, | |
path | |
].join('/').replaceAll('//', '/'); | |
log(s3Path); | |
} | |
}) | |
it("should append /index.html when url has no file extension", () => { | |
const url = new URL("http://www.example.com"); | |
expect(url.pathname).toBe("/") | |
normalizePath(url); | |
expect(url.pathname).toBe("/index.html") | |
}) | |
xit("should append index.html when url has no file extension or trailing slash", () => { | |
const url = new URL("http://www.example.com/"); | |
normalizePath(url); | |
expect(url.pathname).toBe("/index.html") | |
}) | |
xit("should do nothing when called with a file extension", () => { | |
const url = new URL("http://www.example.com/file.jpg"); | |
normalizePath(url); | |
expect(url.pathname).toBe("/file.jpg") | |
}) | |
it("should be 42", () => { | |
expect(hashCode('foofffff')).toBe(0); | |
}); | |
// it("should be the same reference", () => { | |
// let ref = new Vec3(); | |
// expect<Vec3>(ref).toBe(ref, "Reference Equality"); | |
// }); | |
// it("should perform a memory comparison", () => { | |
// let a = new Vec3(1, 2, 3); | |
// let b = new Vec3(1, 2, 3); | |
// expect<Vec3>(a).toStrictEqual( | |
// b, | |
// "a and b have the same values, (discluding child references)", | |
// ); | |
// }); | |
// it("should compare strings", () => { | |
// expect<string>("a=" + "200").toBe("a=200", "both strings are equal"); | |
// }); | |
// it("should compare values", () => { | |
// expect<i32>(10).toBeLessThan(200); | |
// expect<i32>(1000).toBeGreaterThan(200); | |
// expect<i32>(1000).toBeGreaterThanOrEqual(1000); | |
// expect<i32>(1000).toBeLessThanOrEqual(1000); | |
// }); | |
// it("can log some values to the console", () => { | |
// log<string>("Hello world!"); // strings! | |
// log<f64>(3.1415); // floats! | |
// log<u8>(244); // integers! | |
// log<u64>(0xffffffff); // long values! | |
// log<ArrayBuffer>(new ArrayBuffer(50)); // bytes! | |
// }); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment