Skip to content

feat: fastly.sdkVersion implementation #776

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import { pass, assert } from "./assertions.js";
import { routes } from "./routes.js";
import { sdkVersion } from "fastly:experimental";

routes.set("/fastly/now", function () {
let error = assert(typeof fastly.now, 'function', 'typeof fastly.now')
Expand All @@ -22,3 +23,13 @@ routes.set("/fastly/now", function () {

return pass()
})

routes.set("/fastly/version", function () {
let error = assert(typeof fastly.sdkVersion, 'string', 'typeof fastly.sdkVersion')
if (error) { return error }

error = assert(fastly.sdkVersion, sdkVersion, 'fastly.sdkVersion matches fastly:experimental#sdkVersion')
if (error) { return error }

return pass()
})
2 changes: 1 addition & 1 deletion integration-tests/js-compute/fixtures/app/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import "./dynamic-backend.js"
import "./edge-rate-limiter.js"
import "./env.js"
import "./fanout.js"
import "./fastly-now.js"
import "./fastly-global.js"
import "./fetch-errors.js"
import "./geoip.js"
import "./headers.js"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@
"GET /backend/health/happy-path-backend-exists",
"GET /backend/health/happy-path-backend-does-not-exist",
"GET /env",
"GET /fastly/version",
"GET /multiple-set-cookie/response-init",
"GET /multiple-set-cookie/response-direct",
"GET /request/clone/called-as-constructor",
Expand Down
10 changes: 10 additions & 0 deletions integration-tests/js-compute/fixtures/app/tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -3058,6 +3058,16 @@
"status": 200
}
},
"GET /fastly/version": {
"environments": ["compute", "viceroy"],
"downstream_request": {
"method": "GET",
"pathname": "/fastly/version"
},
"downstream_response": {
"status": 200
}
},
"GET /fastly/getgeolocationforipaddress/interface": {
"environments": ["compute"],
"downstream_request": {
Expand Down
2 changes: 1 addition & 1 deletion integration-tests/js-compute/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ if (!local) {
const setupPath = join(fixturePath, 'setup.js')
if (existsSync(setupPath)) {
core.startGroup('Extra set-up steps for the service')
await zx`${setupPath}`
await zx`${setupPath}${starlingmonkey ? ' --starlingmonkey' : ''}`
await sleep(60)
core.endGroup()
}
Expand Down
135 changes: 73 additions & 62 deletions runtime/fastly/builtins/fastly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ const JSErrorFormatString *FastlyGetErrorMessage(void *userRef, unsigned errorNu

namespace {

api::Engine *ENGINE;

bool enableDebugLogging(JSContext *cx, unsigned argc, JS::Value *vp) {
JS::CallArgs args = CallArgsFromVp(argc, vp);
if (!args.requireAtLeast(cx, __func__, 1))
Expand All @@ -51,6 +49,13 @@ JS::PersistentRooted<JSObject *> Fastly::baseURL;
JS::PersistentRooted<JSString *> Fastly::defaultBackend;
bool Fastly::allowDynamicBackends = false;

bool Fastly::version_get(JSContext *cx, unsigned argc, JS::Value *vp) {
JS::CallArgs args = CallArgsFromVp(argc, vp);
JS::RootedString version_str(cx, JS_NewStringCopyN(cx, RUNTIME_VERSION, strlen(RUNTIME_VERSION)));
args.rval().setString(version_str);
return true;
}

bool Env::env_get(JSContext *cx, unsigned argc, JS::Value *vp) {
JS::CallArgs args = CallArgsFromVp(argc, vp);
if (!args.requireAtLeast(cx, "fastly.env.get", 1))
Expand Down Expand Up @@ -306,145 +311,151 @@ const JSPropertySpec Fastly::properties[] = {
JS_PSGS("defaultBackend", defaultBackend_get, defaultBackend_set, JSPROP_ENUMERATE),
JS_PSGS("allowDynamicBackends", allowDynamicBackends_get, allowDynamicBackends_set,
JSPROP_ENUMERATE),
JS_PSG("sdkVersion", version_get, JSPROP_ENUMERATE),
JS_PS_END};

bool install(api::Engine *engine) {
ENGINE = engine;
JS::RootedObject fastly(ENGINE->cx(), JS_NewPlainObject(ENGINE->cx()));
JS::RootedObject fastly(engine->cx(), JS_NewPlainObject(engine->cx()));
if (!fastly) {
return false;
}

Fastly::env.init(ENGINE->cx(), Env::create(ENGINE->cx()));
Fastly::env.init(engine->cx(), Env::create(engine->cx()));
if (!Fastly::env) {
return false;
}

Fastly::baseURL.init(ENGINE->cx());
Fastly::defaultBackend.init(ENGINE->cx());
Fastly::baseURL.init(engine->cx());
Fastly::defaultBackend.init(engine->cx());

if (!JS_DefineProperty(ENGINE->cx(), ENGINE->global(), "fastly", fastly, 0)) {
if (!JS_DefineProperty(engine->cx(), engine->global(), "fastly", fastly, 0)) {
return false;
}

// fastly:env
RootedValue env_get(ENGINE->cx());
if (!JS_GetProperty(ENGINE->cx(), Fastly::env, "get", &env_get)) {
RootedValue env_get(engine->cx());
if (!JS_GetProperty(engine->cx(), Fastly::env, "get", &env_get)) {
return false;
}
RootedObject env_builtin(ENGINE->cx(), JS_NewObject(ENGINE->cx(), nullptr));
if (!JS_SetProperty(ENGINE->cx(), env_builtin, "env", env_get)) {
RootedObject env_builtin(engine->cx(), JS_NewObject(engine->cx(), nullptr));
if (!JS_SetProperty(engine->cx(), env_builtin, "env", env_get)) {
return false;
}
RootedValue env_builtin_val(ENGINE->cx(), JS::ObjectValue(*env_builtin));
if (!ENGINE->define_builtin_module("fastly:env", env_builtin_val)) {
RootedValue env_builtin_val(engine->cx(), JS::ObjectValue(*env_builtin));
if (!engine->define_builtin_module("fastly:env", env_builtin_val)) {
return false;
}

// fastly:experimental
RootedObject experimental(ENGINE->cx(), JS_NewObject(ENGINE->cx(), nullptr));
RootedValue experimental_val(ENGINE->cx(), JS::ObjectValue(*experimental));
RootedObject experimental(engine->cx(), JS_NewObject(engine->cx(), nullptr));
RootedValue experimental_val(engine->cx(), JS::ObjectValue(*experimental));
// TODO(GB): implement includeBytes
if (!JS_SetProperty(ENGINE->cx(), experimental, "includeBytes", experimental_val)) {
if (!JS_SetProperty(engine->cx(), experimental, "includeBytes", experimental_val)) {
return false;
}
auto set_default_backend =
JS_NewFunction(ENGINE->cx(), &Fastly::defaultBackend_set, 1, 0, "setDefaultBackend");
RootedObject set_default_backend_obj(ENGINE->cx(), JS_GetFunctionObject(set_default_backend));
RootedValue set_default_backend_val(ENGINE->cx(), ObjectValue(*set_default_backend_obj));
if (!JS_SetProperty(ENGINE->cx(), experimental, "setDefaultBackend", set_default_backend_val)) {
JS_NewFunction(engine->cx(), &Fastly::defaultBackend_set, 1, 0, "setDefaultBackend");
RootedObject set_default_backend_obj(engine->cx(), JS_GetFunctionObject(set_default_backend));
RootedValue set_default_backend_val(engine->cx(), ObjectValue(*set_default_backend_obj));
if (!JS_SetProperty(engine->cx(), experimental, "setDefaultBackend", set_default_backend_val)) {
return false;
}
auto allow_dynamic_backends =
JS_NewFunction(ENGINE->cx(), &Fastly::allowDynamicBackends_set, 1, 0, "allowDynamicBackends");
RootedObject allow_dynamic_backends_obj(ENGINE->cx(),
JS_NewFunction(engine->cx(), &Fastly::allowDynamicBackends_set, 1, 0, "allowDynamicBackends");
RootedObject allow_dynamic_backends_obj(engine->cx(),
JS_GetFunctionObject(allow_dynamic_backends));
RootedValue allow_dynamic_backends_val(ENGINE->cx(), ObjectValue(*allow_dynamic_backends_obj));
if (!JS_SetProperty(ENGINE->cx(), experimental, "allowDynamicBackends",
RootedValue allow_dynamic_backends_val(engine->cx(), ObjectValue(*allow_dynamic_backends_obj));
if (!JS_SetProperty(engine->cx(), experimental, "allowDynamicBackends",
allow_dynamic_backends_val)) {
return false;
}
if (!ENGINE->define_builtin_module("fastly:experimental", experimental_val)) {
RootedString version_str(
engine->cx(), JS_NewStringCopyN(engine->cx(), RUNTIME_VERSION, strlen(RUNTIME_VERSION)));
RootedValue version_str_val(engine->cx(), StringValue(version_str));
if (!JS_SetProperty(engine->cx(), experimental, "sdkVersion", version_str_val)) {
return false;
}
if (!engine->define_builtin_module("fastly:experimental", experimental_val)) {
return false;
}

// TODO(GB): all of the following builtin modules are just placeholder shapes for now
if (!ENGINE->define_builtin_module("fastly:body", env_builtin_val)) {
if (!engine->define_builtin_module("fastly:body", env_builtin_val)) {
return false;
}
RootedObject cache(ENGINE->cx(), JS_NewObject(ENGINE->cx(), nullptr));
RootedValue cache_val(ENGINE->cx(), JS::ObjectValue(*cache));
if (!JS_SetProperty(ENGINE->cx(), cache, "CoreCache", cache_val)) {
RootedObject cache(engine->cx(), JS_NewObject(engine->cx(), nullptr));
RootedValue cache_val(engine->cx(), JS::ObjectValue(*cache));
if (!JS_SetProperty(engine->cx(), cache, "CoreCache", cache_val)) {
return false;
}
if (!JS_SetProperty(ENGINE->cx(), cache, "CacheEntry", cache_val)) {
if (!JS_SetProperty(engine->cx(), cache, "CacheEntry", cache_val)) {
return false;
}
if (!JS_SetProperty(ENGINE->cx(), cache, "CacheEntry", cache_val)) {
if (!JS_SetProperty(engine->cx(), cache, "CacheEntry", cache_val)) {
return false;
}
if (!JS_SetProperty(ENGINE->cx(), cache, "SimpleCache", cache_val)) {
if (!JS_SetProperty(engine->cx(), cache, "SimpleCache", cache_val)) {
return false;
}
if (!ENGINE->define_builtin_module("fastly:cache", cache_val)) {
if (!engine->define_builtin_module("fastly:cache", cache_val)) {
return false;
}
if (!ENGINE->define_builtin_module("fastly:config-store", env_builtin_val)) {
if (!engine->define_builtin_module("fastly:config-store", env_builtin_val)) {
return false;
}
RootedObject device_device(ENGINE->cx(), JS_NewObject(ENGINE->cx(), nullptr));
RootedValue device_device_val(ENGINE->cx(), JS::ObjectValue(*device_device));
if (!JS_SetProperty(ENGINE->cx(), device_device, "Device", device_device_val)) {
RootedObject device_device(engine->cx(), JS_NewObject(engine->cx(), nullptr));
RootedValue device_device_val(engine->cx(), JS::ObjectValue(*device_device));
if (!JS_SetProperty(engine->cx(), device_device, "Device", device_device_val)) {
return false;
}
if (!ENGINE->define_builtin_module("fastly:device", device_device_val)) {
if (!engine->define_builtin_module("fastly:device", device_device_val)) {
return false;
}
RootedObject dictionary(ENGINE->cx(), JS_NewObject(ENGINE->cx(), nullptr));
RootedValue dictionary_val(ENGINE->cx(), JS::ObjectValue(*dictionary));
if (!JS_SetProperty(ENGINE->cx(), dictionary, "Dictionary", dictionary_val)) {
RootedObject dictionary(engine->cx(), JS_NewObject(engine->cx(), nullptr));
RootedValue dictionary_val(engine->cx(), JS::ObjectValue(*dictionary));
if (!JS_SetProperty(engine->cx(), dictionary, "Dictionary", dictionary_val)) {
return false;
}
if (!ENGINE->define_builtin_module("fastly:dictionary", dictionary_val)) {
if (!engine->define_builtin_module("fastly:dictionary", dictionary_val)) {
return false;
}
RootedObject edge_rate_limiter(ENGINE->cx(), JS_NewObject(ENGINE->cx(), nullptr));
RootedValue edge_rate_limiter_val(ENGINE->cx(), JS::ObjectValue(*edge_rate_limiter));
if (!JS_SetProperty(ENGINE->cx(), edge_rate_limiter, "RateCounter", edge_rate_limiter_val)) {
RootedObject edge_rate_limiter(engine->cx(), JS_NewObject(engine->cx(), nullptr));
RootedValue edge_rate_limiter_val(engine->cx(), JS::ObjectValue(*edge_rate_limiter));
if (!JS_SetProperty(engine->cx(), edge_rate_limiter, "RateCounter", edge_rate_limiter_val)) {
return false;
}
if (!JS_SetProperty(ENGINE->cx(), edge_rate_limiter, "PenaltyBox", edge_rate_limiter_val)) {
if (!JS_SetProperty(engine->cx(), edge_rate_limiter, "PenaltyBox", edge_rate_limiter_val)) {
return false;
}
if (!JS_SetProperty(ENGINE->cx(), edge_rate_limiter, "EdgeRateLimiter", edge_rate_limiter_val)) {
if (!JS_SetProperty(engine->cx(), edge_rate_limiter, "EdgeRateLimiter", edge_rate_limiter_val)) {
return false;
}
if (!ENGINE->define_builtin_module("fastly:edge-rate-limiter", edge_rate_limiter_val)) {
if (!engine->define_builtin_module("fastly:edge-rate-limiter", edge_rate_limiter_val)) {
return false;
}
RootedObject fanout(ENGINE->cx(), JS_NewObject(ENGINE->cx(), nullptr));
RootedValue fanout_val(ENGINE->cx(), JS::ObjectValue(*fanout));
if (!JS_SetProperty(ENGINE->cx(), fanout, "createFanoutHandoff", fanout_val)) {
RootedObject fanout(engine->cx(), JS_NewObject(engine->cx(), nullptr));
RootedValue fanout_val(engine->cx(), JS::ObjectValue(*fanout));
if (!JS_SetProperty(engine->cx(), fanout, "createFanoutHandoff", fanout_val)) {
return false;
}
if (!ENGINE->define_builtin_module("fastly:fanout", fanout_val)) {
if (!engine->define_builtin_module("fastly:fanout", fanout_val)) {
return false;
}
if (!ENGINE->define_builtin_module("fastly:geolocation", env_builtin_val)) {
if (!engine->define_builtin_module("fastly:geolocation", env_builtin_val)) {
return false;
}
RootedObject kv_store(ENGINE->cx(), JS_NewObject(ENGINE->cx(), nullptr));
RootedValue kv_store_val(ENGINE->cx(), JS::ObjectValue(*kv_store));
if (!JS_SetProperty(ENGINE->cx(), kv_store, "KVStore", kv_store_val)) {
RootedObject kv_store(engine->cx(), JS_NewObject(engine->cx(), nullptr));
RootedValue kv_store_val(engine->cx(), JS::ObjectValue(*kv_store));
if (!JS_SetProperty(engine->cx(), kv_store, "KVStore", kv_store_val)) {
return false;
}
if (!ENGINE->define_builtin_module("fastly:kv-store", kv_store_val)) {
if (!engine->define_builtin_module("fastly:kv-store", kv_store_val)) {
return false;
}
if (!ENGINE->define_builtin_module("fastly:logger", env_builtin_val)) {
if (!engine->define_builtin_module("fastly:logger", env_builtin_val)) {
return false;
}
if (!ENGINE->define_builtin_module("fastly:secret-store", env_builtin_val)) {
if (!engine->define_builtin_module("fastly:secret-store", env_builtin_val)) {
return false;
}

Expand All @@ -463,8 +474,8 @@ bool install(api::Engine *engine) {
// options.getExperimentalHighResolutionTimeMethodsEnabled() ? nowfn : end,
end};

return JS_DefineFunctions(ENGINE->cx(), fastly, methods) &&
JS_DefineProperties(ENGINE->cx(), fastly, Fastly::properties);
return JS_DefineFunctions(engine->cx(), fastly, methods) &&
JS_DefineProperties(engine->cx(), fastly, Fastly::properties);
}

} // namespace fastly::fastly
3 changes: 3 additions & 0 deletions runtime/fastly/builtins/fastly.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ using namespace builtins;

namespace fastly::fastly {

#define RUNTIME_VERSION "starlingmonkey-dev"

class Env : public BuiltinNoConstructor<Env> {
private:
static bool env_get(JSContext *cx, unsigned argc, JS::Value *vp);
Expand Down Expand Up @@ -53,6 +55,7 @@ class Fastly : public BuiltinNoConstructor<Fastly> {
// static bool getGeolocationForIpAddress(JSContext *cx, unsigned argc, JS::Value *vp);
// static bool getLogger(JSContext *cx, unsigned argc, JS::Value *vp);
// static bool includeBytes(JSContext *cx, unsigned argc, JS::Value *vp);
static bool version_get(JSContext *cx, unsigned argc, JS::Value *vp);
static bool env_get(JSContext *cx, unsigned argc, JS::Value *vp);
static bool baseURL_get(JSContext *cx, unsigned argc, JS::Value *vp);
static bool baseURL_set(JSContext *cx, unsigned argc, JS::Value *vp);
Expand Down
8 changes: 8 additions & 0 deletions runtime/js-compute-runtime/builtins/fastly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,13 @@ bool Fastly::env_get(JSContext *cx, unsigned argc, JS::Value *vp) {
return true;
}

bool Fastly::version_get(JSContext *cx, unsigned argc, JS::Value *vp) {
JS::CallArgs args = CallArgsFromVp(argc, vp);
JS::RootedString version_str(cx, JS_NewStringCopyN(cx, RUNTIME_VERSION, strlen(RUNTIME_VERSION)));
args.rval().setString(version_str);
return true;
}

bool Fastly::baseURL_get(JSContext *cx, unsigned argc, JS::Value *vp) {
JS::CallArgs args = CallArgsFromVp(argc, vp);
args.rval().setObjectOrNull(baseURL);
Expand Down Expand Up @@ -262,6 +269,7 @@ const JSPropertySpec Fastly::properties[] = {
JS_PSGS("defaultBackend", defaultBackend_get, defaultBackend_set, JSPROP_ENUMERATE),
JS_PSGS("allowDynamicBackends", allowDynamicBackends_get, allowDynamicBackends_set,
JSPROP_ENUMERATE),
JS_PSG("sdkVersion", version_get, JSPROP_ENUMERATE),
JS_PS_END};

bool Fastly::create(JSContext *cx, JS::HandleObject global, FastlyOptions options) {
Expand Down
1 change: 1 addition & 0 deletions runtime/js-compute-runtime/builtins/fastly.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class Fastly : public BuiltinNoConstructor<Fastly> {
static bool getLogger(JSContext *cx, unsigned argc, JS::Value *vp);
static bool includeBytes(JSContext *cx, unsigned argc, JS::Value *vp);
static bool env_get(JSContext *cx, unsigned argc, JS::Value *vp);
static bool version_get(JSContext *cx, unsigned argc, JS::Value *vp);
static bool baseURL_get(JSContext *cx, unsigned argc, JS::Value *vp);
static bool baseURL_set(JSContext *cx, unsigned argc, JS::Value *vp);
static bool defaultBackend_get(JSContext *cx, unsigned argc, JS::Value *vp);
Expand Down
2 changes: 2 additions & 0 deletions runtime/js-compute-runtime/js-compute-builtins.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ const JSErrorFormatString js_ErrorFormatString[JSErrNum_Limit] = {

#include "host_interface/host_api.h"

#define RUNTIME_VERSION "3.13.2-dev"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We talked about this briefly earlier, but it would be great to figure out how to get release-please to keep this in sync.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will follow up as soon as I've worked out the release process.


const JSErrorFormatString *GetErrorMessage(void *userRef, unsigned errorNumber);

JSObject *PromiseRejectedWithPendingError(JSContext *cx);
Expand Down
1 change: 1 addition & 0 deletions src/bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const enableDebugLogging = globalThis.fastly.enableDebugLogging;
export const setBaseURL = Object.getOwnPropertyDescriptor(globalThis.fastly, 'baseURL').set;
export const setDefaultBackend = Object.getOwnPropertyDescriptor(globalThis.fastly, 'defaultBackend').set;
export const allowDynamicBackends = Object.getOwnPropertyDescriptor(globalThis.fastly, 'allowDynamicBackends').set;
export const sdkVersion = globalThis.fastly.sdkVersion;
`
}
}
Expand Down
7 changes: 7 additions & 0 deletions types/experimental.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@
* @experimental
*/
declare module "fastly:experimental" {
/**
* JavaScript SDK version string for the JS runtime engine build.
*
* @experimental
* @hidden
*/
export const sdkVersion: string;
/**
* @experimental
* @hidden
Expand Down
Loading
Loading