Skip to content

Commit d605df9

Browse files
author
Jake Champion
committed
feat: Add ConfigStore class
ConfigStore will eventually be the new name for Dictionary, but for now we will keep the Dictionary class around to allow customers to be able to slowly migrate to the new class.
1 parent bd799bd commit d605df9

File tree

9 files changed

+209
-23
lines changed

9 files changed

+209
-23
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#include "config-store.h"
2+
#include "host_call.h"
3+
4+
namespace builtins {
5+
6+
ConfigStoreHandle ConfigStore::config_store_handle(JSObject *obj) {
7+
JS::Value val = JS::GetReservedSlot(obj, ConfigStore::Slots::Handle);
8+
return ConfigStoreHandle{static_cast<uint32_t>(val.toInt32())};
9+
}
10+
11+
bool ConfigStore::get(JSContext *cx, unsigned argc, JS::Value *vp) {
12+
METHOD_HEADER(1)
13+
14+
size_t name_len;
15+
JS::UniqueChars name = encode(cx, args[0], &name_len);
16+
17+
OwnedHostCallBuffer buffer;
18+
size_t nwritten = 0;
19+
int status = xqd_config_store_get(ConfigStore::config_store_handle(self), name.get(), name_len,
20+
buffer.get(), CONFIG_STORE_ENTRY_MAX_LEN, &nwritten);
21+
// Status code 10 indicates the key wasn't found, so we return null.
22+
if (status == 10) {
23+
args.rval().setNull();
24+
return true;
25+
}
26+
27+
// Ensure that we throw an exception for all unexpected host errors.
28+
if (!HANDLE_RESULT(cx, status))
29+
return false;
30+
31+
JS::RootedString text(cx, JS_NewStringCopyUTF8N(cx, JS::UTF8Chars(buffer.get(), nwritten)));
32+
if (!text)
33+
return false;
34+
35+
args.rval().setString(text);
36+
return true;
37+
}
38+
39+
const JSFunctionSpec ConfigStore::methods[] = {JS_FN("get", get, 1, JSPROP_ENUMERATE), JS_FS_END};
40+
41+
const JSPropertySpec ConfigStore::properties[] = {JS_PS_END};
42+
43+
bool ConfigStore::constructor(JSContext *cx, unsigned argc, JS::Value *vp) {
44+
REQUEST_HANDLER_ONLY("The ConfigStore builtin");
45+
CTOR_HEADER("ConfigStore", 1);
46+
47+
size_t name_len;
48+
JS::UniqueChars name = encode(cx, args[0], &name_len);
49+
JS::RootedObject config_store(cx, JS_NewObjectForConstructor(cx, &class_, args));
50+
ConfigStoreHandle dict_handle = {INVALID_HANDLE};
51+
if (!HANDLE_RESULT(cx, xqd_config_store_open(name.get(), name_len, &dict_handle)))
52+
return false;
53+
54+
JS::SetReservedSlot(config_store, ConfigStore::Slots::Handle,
55+
JS::Int32Value((int)dict_handle.handle));
56+
if (!config_store)
57+
return false;
58+
args.rval().setObject(*config_store);
59+
return true;
60+
}
61+
62+
bool ConfigStore::init_class(JSContext *cx, JS::HandleObject global) {
63+
return BuiltinImpl<ConfigStore>::init_class_impl(cx, global);
64+
}
65+
66+
} // namespace builtins
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#ifndef JS_COMPUTE_RUNTIME_CONFIG_STORE_H
2+
#define JS_COMPUTE_RUNTIME_CONFIG_STORE_H
3+
4+
#include "builtin.h"
5+
6+
namespace builtins {
7+
8+
class ConfigStore : public BuiltinImpl<ConfigStore> {
9+
private:
10+
public:
11+
static constexpr const char *class_name = "ConfigStore";
12+
static const int ctor_length = 1;
13+
enum Slots { Handle, Count };
14+
15+
static const JSFunctionSpec methods[];
16+
static const JSPropertySpec properties[];
17+
18+
static bool get(JSContext *cx, unsigned argc, JS::Value *vp);
19+
20+
static ConfigStoreHandle config_store_handle(JSObject *obj);
21+
static bool constructor(JSContext *cx, unsigned argc, JS::Value *vp);
22+
23+
static bool init_class(JSContext *cx, JS::HandleObject global);
24+
};
25+
26+
} // namespace builtins
27+
28+
#endif

c-dependencies/js-compute-runtime/js-compute-builtins.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "builtin.h"
3939
#include "builtins/cache-override.h"
4040
#include "builtins/compression-stream.h"
41+
#include "builtins/config-store.h"
4142
#include "builtins/console.h"
4243
#include "builtins/crypto.h"
4344
#include "builtins/decompression-stream.h"
@@ -4446,6 +4447,8 @@ bool define_fastly_sys(JSContext *cx, HandleObject global) {
44464447
return false;
44474448
if (!Response::init_class(cx, global))
44484449
return false;
4450+
if (!builtins::ConfigStore::init_class(cx, global))
4451+
return false;
44494452
if (!builtins::Dictionary::init_class(cx, global))
44504453
return false;
44514454
if (!Headers::init_class(cx, global))

c-dependencies/js-compute-runtime/xqd.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ extern "C" {
1616
#define HEADER_MAX_LEN 69000
1717
#define METHOD_MAX_LEN 1024
1818
#define URI_MAX_LEN 8192
19-
#define DICTIONARY_ENTRY_MAX_LEN 8000
19+
#define CONFIG_STORE_ENTRY_MAX_LEN 8000
20+
#define DICTIONARY_ENTRY_MAX_LEN CONFIG_STORE_ENTRY_MAX_LEN
2021

2122
// TODO ACF 2020-01-17: these aren't very C-friendly names
2223
typedef struct {
@@ -43,6 +44,10 @@ typedef struct {
4344
uint32_t handle;
4445
} DictionaryHandle;
4546

47+
typedef struct {
48+
uint32_t handle;
49+
} ConfigStoreHandle;
50+
4651
typedef struct {
4752
uint32_t handle;
4853
} ObjectStoreHandle;
@@ -298,6 +303,13 @@ WASM_IMPORT("fastly_dictionary", "get")
298303
int xqd_dictionary_get(DictionaryHandle dict_handle, const char *key, size_t key_len, char *value,
299304
size_t value_max_len, size_t *nwritten);
300305

306+
WASM_IMPORT("fastly_dictionary", "open")
307+
int xqd_config_store_open(const char *name, size_t name_len, ConfigStoreHandle *dict_handle_out);
308+
309+
WASM_IMPORT("fastly_dictionary", "get")
310+
int xqd_config_store_get(ConfigStoreHandle dict_handle, const char *key, size_t key_len,
311+
char *value, size_t value_max_len, size_t *nwritten);
312+
301313
// Module fastly_object_store
302314
WASM_IMPORT("fastly_object_store", "open")
303315
int fastly_object_store_open(const char *name, size_t name_len,
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
addEventListener("fetch", (event) => {
2+
let config = new ConfigStore("testconfig");
3+
let twitterValue = config.get("twitter");
4+
if (twitterValue) {
5+
event.respondWith(new Response(twitterValue));
6+
} else {
7+
event.respondWith(new Response("twitter key does not exist", {
8+
status: 500,
9+
}));
10+
}
11+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# This file describes a Fastly Compute@Edge package. To learn more visit:
2+
# https://developer.fastly.com/reference/fastly-toml/
3+
4+
authors = ["[email protected]"]
5+
description = ""
6+
language = "other"
7+
manifest_version = 2
8+
name = "config-store"
9+
service_id = ""
10+
11+
[scripts]
12+
build = "../../../../target/release/js-compute-runtime"
13+
14+
[local_server]
15+
[local_server.dictionaries]
16+
[local_server.dictionaries.testconfig]
17+
format = "inline-toml"
18+
[local_server.dictionaries.testconfig.contents]
19+
"twitter" = "https://twitter.com/fastly"
20+
21+
[setup]
22+
[setup.dictionaries]
23+
[setup.dictionaries.testconfig]
24+
[setup.dictionaries.testconfig.items]
25+
[setup.dictionaries.testconfig.items.twitter]
26+
value = "https://twitter.com/fastly"
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"GET /": {
3+
"environments": ["viceroy", "c@e"],
4+
"downstream_request": {
5+
"method": "GET",
6+
"pathname": "/"
7+
},
8+
"downstream_response": {
9+
"status": 200,
10+
"body": "https://twitter.com/fastly"
11+
}
12+
}
13+
}

sdk/js-compute/index.d.ts

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,28 @@ declare interface ClientInfo {
144144
readonly geo: Geolocation;
145145
}
146146

147+
/**
148+
* Class for accessing [Fastly Edge Dictionaries](https://docs.fastly.com/en/guides/about-edge-dictionaries).
149+
*
150+
* **Note**: Can only be used when processing requests, not during build-time initialization.
151+
*/
152+
declare class ConfigStore {
153+
/**
154+
* Creates a new ConfigStore object
155+
*/
156+
constructor(name: string);
157+
/**
158+
* Get a value for a key in the config-store.
159+
*/
160+
get(key: string): string;
161+
}
162+
163+
147164
/**
148165
* Class for accessing [Fastly Edge Dictionaries](https://docs.fastly.com/en/guides/about-edge-dictionaries).
149166
*
150167
* **Note**: Can only be used when processing requests, not during build-time initialization.
168+
* @deprecated This class has been renamed `ConfigStore`. Replace `Dictionary` with `ConfigStore` in your code to avoid having to migrate in the future when `Dictionary` is removed.
151169
*/
152170
declare class Dictionary {
153171
/**
@@ -352,28 +370,28 @@ declare class ObjectStore {
352370
* Class for interacting with a [Fastly Object-store](https://developer.fastly.com/reference/api/object-store/) entry.
353371
*/
354372
declare interface ObjectStoreEntry {
355-
/**
356-
* A ReadableStream with the contents of the entry.
357-
*/
358-
get body(): ReadableStream;
359-
/**
360-
* A boolean value that indicates whether the body has been read from already.
361-
*/
362-
get bodyUsed(): boolean;
363-
/**
364-
* Reads the body and returns it as a promise that resolves with a string.
365-
* The response is always decoded using UTF-8.
366-
*/
367-
text(): Promise<string>;
368-
/**
369-
* Reads the body and returns it as a promise that resolves with the result of parsing the body text as JSON.
370-
*/
371-
json(): Promise<object>;
372-
/**
373-
* Reads the body and returns it as a promise that resolves with an ArrayBuffer.
374-
*/
375-
arrayBuffer(): Promise<ArrayBuffer>;
376-
// And eventually formData and blob once we support them on Request and Response, too.
373+
/**
374+
* A ReadableStream with the contents of the entry.
375+
*/
376+
get body(): ReadableStream;
377+
/**
378+
* A boolean value that indicates whether the body has been read from already.
379+
*/
380+
get bodyUsed(): boolean;
381+
/**
382+
* Reads the body and returns it as a promise that resolves with a string.
383+
* The response is always decoded using UTF-8.
384+
*/
385+
text(): Promise<string>;
386+
/**
387+
* Reads the body and returns it as a promise that resolves with the result of parsing the body text as JSON.
388+
*/
389+
json(): Promise<object>;
390+
/**
391+
* Reads the body and returns it as a promise that resolves with an ArrayBuffer.
392+
*/
393+
arrayBuffer(): Promise<ArrayBuffer>;
394+
// And eventually formData and blob once we support them on Request and Response, too.
377395
}
378396

379397
/**

sdk/js-compute/index.test-d.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,15 @@ import {expectError, expectType} from 'tsd';
133133
expectError(client.geo = {} as Geolocation)
134134
}
135135

136+
// ConfigStore
137+
{
138+
expectError(new ConfigStore())
139+
expectError(ConfigStore('example'))
140+
expectError(ConfigStore())
141+
expectType<ConfigStore>(new ConfigStore('example'))
142+
expectType<(key:string) => string>(new ConfigStore('example').get)
143+
}
144+
136145
// Dictionary
137146
{
138147
expectError(new Dictionary())

0 commit comments

Comments
 (0)