diff --git a/.github/workflows/sanity.yml b/.github/workflows/sanity.yml index 1b10402e..62074694 100644 --- a/.github/workflows/sanity.yml +++ b/.github/workflows/sanity.yml @@ -33,4 +33,5 @@ jobs: - name: Run test run: | + export GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} go test -v ./... diff --git a/rcap/authentication.go b/rcap/authentication.go new file mode 100644 index 00000000..4c3ecb28 --- /dev/null +++ b/rcap/authentication.go @@ -0,0 +1,81 @@ +package rcap + +import ( + "os" + "strings" +) + +// AuthProvider is a provider for various kinds of auth +type AuthProvider interface { + HeaderForDomain(string) *AuthHeader +} + +// AuthHeader is an HTTP header designed to authenticate requests +type AuthHeader struct { + HeaderType string `json:"headerType"` + Value string `json:"value"` +} + +// AuthProviderConfig is a config for the default auth provider +type AuthProviderConfig struct { + // Headers is a map between domains and auth header that should be added to requests to those domains + Headers map[string]AuthHeader `json:"headers"` +} + +type defaultAuthProvider struct { + config *AuthProviderConfig + + augmentedHeaders map[string]AuthHeader +} + +// DefaultAuthProvider creates the default static auth provider +func DefaultAuthProvider(config *AuthProviderConfig) AuthProvider { + ap := &defaultAuthProvider{ + config: config, + augmentedHeaders: map[string]AuthHeader{}, + } + + return ap +} + +// HeadersForDomain returns the appropriate auth headers for the given domain +func (ap *defaultAuthProvider) HeaderForDomain(domain string) *AuthHeader { + header, ok := ap.augmentedHeaders[domain] + if !ok { + if ap.config == nil { + return nil + } + + origignalHeader, exists := ap.config.Headers[domain] + if !exists { + return nil + } + + augmented := augmentHeaderFromEnv(origignalHeader) + + ap.augmentedHeaders[domain] = augmented + header = augmented + } + + return &header +} + +// augmentHeadersFromEnv takes a an AuthHeader and replaces and `env()` values with their representative values from the environment +func augmentHeaderFromEnv(header AuthHeader) AuthHeader { + // turn env(SOME_HEADER_KEY) into SOME_HEADER_KEY + if strings.HasPrefix(header.Value, "env(") && strings.HasSuffix(header.Value, ")") { + headerKey := strings.TrimPrefix(header.Value, "env(") + headerKey = strings.TrimSuffix(headerKey, ")") + + val := os.Getenv(headerKey) + + augmentedHeader := AuthHeader{ + HeaderType: header.HeaderType, + Value: val, + } + + return augmentedHeader + } + + return header +} diff --git a/rcap/graphql.go b/rcap/graphql.go index acba9a2d..56c3f47a 100644 --- a/rcap/graphql.go +++ b/rcap/graphql.go @@ -6,13 +6,14 @@ import ( "fmt" "io/ioutil" "net/http" + "net/url" "github.com/pkg/errors" ) // GraphQLClient is a GraphQL capability for Reactr Modules type GraphQLClient interface { - Do(endpoint, query string) (*GraphQLResponse, error) + Do(auth AuthProvider, endpoint, query string) (*GraphQLResponse, error) } // defaultGraphQLClient is the default implementation of the GraphQL capability @@ -48,7 +49,7 @@ type GraphQLError struct { Path string `json:"path"` } -func (g *defaultGraphQLClient) Do(endpoint, query string) (*GraphQLResponse, error) { +func (g *defaultGraphQLClient) Do(auth AuthProvider, endpoint, query string) (*GraphQLResponse, error) { r := &GraphQLRequest{ Query: query, Variables: map[string]string{}, @@ -59,6 +60,11 @@ func (g *defaultGraphQLClient) Do(endpoint, query string) (*GraphQLResponse, err return nil, errors.Wrap(err, "failed to Marshal request") } + endpointURL, err := url.Parse(endpoint) + if err != nil { + return nil, errors.Wrap(err, "failed to Parse endpoint") + } + req, err := http.NewRequest(http.MethodPost, endpoint, bytes.NewBuffer(reqBytes)) if err != nil { return nil, errors.Wrap(err, "failed to NewRequest") @@ -66,6 +72,11 @@ func (g *defaultGraphQLClient) Do(endpoint, query string) (*GraphQLResponse, err req.Header.Add("Content-Type", "application/json") + authHeader := auth.HeaderForDomain(endpointURL.Host) + if authHeader.Value != "" { + req.Header.Add("Authorization", fmt.Sprintf("%s %s", authHeader.HeaderType, authHeader.Value)) + } + resp, err := g.client.Do(req) if err != nil { return nil, errors.Wrap(err, "failed to Do") @@ -84,7 +95,7 @@ func (g *defaultGraphQLClient) Do(endpoint, query string) (*GraphQLResponse, err } if resp.StatusCode > 299 { - return gqlResp, errors.New("non-200 HTTP response code") + return gqlResp, fmt.Errorf("non-200 HTTP response code; %s", string(respJSON)) } if gqlResp.Errors != nil && len(gqlResp.Errors) > 0 { diff --git a/rcap/tester/main.go b/rcap/tester/main.go deleted file mode 100644 index 24710c0f..00000000 --- a/rcap/tester/main.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "log" - - "github.com/suborbital/reactr/rcap" -) - -func main() { - gqlClient := rcap.DefaultGraphQLClient() - - resp, err := gqlClient.Do("https://api.rawkode.dev", `{ - allProfiles { - forename - surname - } - } - `) - - if err != nil { - fmt.Println(resp) - log.Fatal(err) - } - - jsonBytes, _ := json.Marshal(resp.Data) - - fmt.Println(string(jsonBytes)) -} diff --git a/rt/capabilities.go b/rt/capabilities.go index af2381a2..1860547b 100644 --- a/rt/capabilities.go +++ b/rt/capabilities.go @@ -10,6 +10,7 @@ var ErrCapabilityNotAvailable = errors.New("capability not available") // Capabilities define the capabilities available to a Runnable type Capabilities struct { + Auth rcap.AuthProvider LoggerSource rcap.LoggerSource HTTPClient rcap.HTTPClient GraphQLClient rcap.GraphQLClient @@ -25,6 +26,7 @@ type Capabilities struct { func defaultCaps(logger *vlog.Logger) Capabilities { caps := Capabilities{ + Auth: rcap.DefaultAuthProvider(nil), // no authentication config is set up by default LoggerSource: rcap.DefaultLoggerSource(logger), HTTPClient: rcap.DefaultHTTPClient(), GraphQLClient: rcap.DefaultGraphQLClient(), diff --git a/rwasm/api_graphql.go b/rwasm/api_graphql.go index b79b34ef..8f502a9b 100644 --- a/rwasm/api_graphql.go +++ b/rwasm/api_graphql.go @@ -36,7 +36,7 @@ func graphql_query(endpointPointer int32, endpointSize int32, queryPointer int32 queryBytes := inst.readMemory(queryPointer, querySize) query := string(queryBytes) - resp, err := inst.ctx.GraphQLClient.Do(endpoint, query) + resp, err := inst.ctx.GraphQLClient.Do(inst.ctx.Auth, endpoint, query) if err != nil { internalLogger.Error(errors.Wrap(err, "failed to GraphQLClient.Do")) return -1 diff --git a/rwasm/testdata/as-graphql/as-graphql.wasm b/rwasm/testdata/as-graphql/as-graphql.wasm index ecfb5c9c..e9fe26a2 100644 Binary files a/rwasm/testdata/as-graphql/as-graphql.wasm and b/rwasm/testdata/as-graphql/as-graphql.wasm differ diff --git a/rwasm/testdata/as-graphql/src/lib.ts b/rwasm/testdata/as-graphql/src/lib.ts index fde01efd..2f3f3082 100755 --- a/rwasm/testdata/as-graphql/src/lib.ts +++ b/rwasm/testdata/as-graphql/src/lib.ts @@ -1,7 +1,7 @@ import { graphQLQuery, logInfo } from "@suborbital/suborbital" export function run(_: ArrayBuffer): ArrayBuffer { - let result = graphQLQuery("https://api.rawkode.dev", "{ allProfiles { forename, surname } }") + let result = graphQLQuery("https://api.github.com/graphql", "{ repository (owner: \"suborbital\", name: \"reactr\") { name, nameWithOwner }}") if (result.byteLength == 0) { return String.UTF8.encode("failed") } diff --git a/rwasm/testdata/as-json/.runnable.yaml b/rwasm/testdata/as-json/.runnable.yaml new file mode 100755 index 00000000..503619d7 --- /dev/null +++ b/rwasm/testdata/as-json/.runnable.yaml @@ -0,0 +1,5 @@ +name: as-json +namespace: default +lang: assemblyscript +version: "" +apiVersion: 0.10.0 diff --git a/rwasm/testdata/as-json/as-json.wasm b/rwasm/testdata/as-json/as-json.wasm new file mode 100644 index 00000000..21d41314 Binary files /dev/null and b/rwasm/testdata/as-json/as-json.wasm differ diff --git a/rwasm/testdata/as-json/asconfig.json b/rwasm/testdata/as-json/asconfig.json new file mode 100755 index 00000000..e4ad736c --- /dev/null +++ b/rwasm/testdata/as-json/asconfig.json @@ -0,0 +1,12 @@ +{ + "targets": { + "release": { + "binaryFile": "as-json.wasm", + "optimizeLevel": 3, + "shrinkLevel": 1, + "converge": false, + "noAssert": false + } + }, + "options": {} +} \ No newline at end of file diff --git a/rwasm/testdata/as-json/package-lock.json b/rwasm/testdata/as-json/package-lock.json new file mode 100644 index 00000000..63d4c740 --- /dev/null +++ b/rwasm/testdata/as-json/package-lock.json @@ -0,0 +1,202 @@ +{ + "name": "as-json", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@assemblyscript/loader": "^0.19", + "@suborbital/suborbital": "^0.10.0", + "json-as": "github:aspkg/as-json" + }, + "devDependencies": { + "assemblyscript": "^0.19" + } + }, + "node_modules/@assemblyscript/loader": { + "version": "0.19.7", + "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.19.7.tgz", + "integrity": "sha512-/BdvOMHnfW3uIHFEgDPRbmA+BoQjuYpjKIZgI3p4sG8Jxgy9UuQW7cQUFx7dRlFUk/SBVCoB8N7412NgKyctqg==" + }, + "node_modules/@suborbital/suborbital": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@suborbital/suborbital/-/suborbital-0.10.0.tgz", + "integrity": "sha512-Ggy58dYcqT3pBTvr6nOFuMyLKgsHFjdbz/B/UjPi0tHOQ3n91g0lwOcsIGq3Rlu01FW7ppseSAKkPe2zhvP67w==", + "dependencies": { + "@assemblyscript/loader": "^0.19" + } + }, + "node_modules/assemblyscript": { + "version": "0.19.7", + "resolved": "https://registry.npmjs.org/assemblyscript/-/assemblyscript-0.19.7.tgz", + "integrity": "sha512-0Vs27D9yIqxbwAx/zp2CNMCgR/Aa2cQlMZO+ctW30rhQ0/UOS/J9PkFXRxUVNMYj0xwEMT/Cq6l9flXO2ZJFcQ==", + "dev": true, + "dependencies": { + "binaryen": "101.0.0-nightly.20210703", + "long": "^4.0.0" + }, + "bin": { + "asc": "bin/asc", + "asinit": "bin/asinit" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/assemblyscript" + } + }, + "node_modules/binaryen": { + "version": "101.0.0-nightly.20210703", + "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-101.0.0-nightly.20210703.tgz", + "integrity": "sha512-7iuRy/aSsd8BlO0ZOG4GqDI8z66UhazD10eeC3i14SOhZJ+MLC3I+BX5ydr3nk9g5j2PDGndxuYvm0gy+VJHYg==", + "dev": true, + "bin": { + "wasm-opt": "bin/wasm-opt" + } + }, + "node_modules/json-as": { + "version": "0.1.1", + "resolved": "git+ssh://git@github.com/aspkg/as-json.git#f5b00039ed9cd0b95f21bec0c85abf5cdbb32e3b", + "license": "MIT", + "dependencies": { + "visitor-as": "^0.6.0" + } + }, + "node_modules/json-as/node_modules/assemblyscript": { + "version": "0.18.32", + "resolved": "https://registry.npmjs.org/assemblyscript/-/assemblyscript-0.18.32.tgz", + "integrity": "sha512-Py6zremwGhO3nSoI/VxyVUzTZfNhTjzNzFDaUdG4JhPJHeG+FzVlEoNCrw4bE5nPc7F+P2DJ8tZQCqIt15ceKw==", + "peer": true, + "dependencies": { + "binaryen": "100.0.0-nightly.20210413", + "long": "^4.0.0" + }, + "bin": { + "asc": "bin/asc", + "asinit": "bin/asinit" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/assemblyscript" + } + }, + "node_modules/json-as/node_modules/binaryen": { + "version": "100.0.0-nightly.20210413", + "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-100.0.0-nightly.20210413.tgz", + "integrity": "sha512-EeGLIxQmJS0xnYl+SH34mNBqVMoixKd9nsE7S7z+CtS9A4eoWn3Qjav+XElgunUgXIHAI5yLnYT2TUGnLX2f1w==", + "peer": true, + "bin": { + "wasm-opt": "bin/wasm-opt" + } + }, + "node_modules/json-as/node_modules/visitor-as": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/visitor-as/-/visitor-as-0.6.0.tgz", + "integrity": "sha512-4WcnwCLXWjhNkwJj9gSqh46sdIv9CyIvnSuwr61OOfrGCtN2mKcW5KE828OeEr1rYjEy0Z/CIdPBJKJRLsUgDA==", + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "ts-mixer": "^5.4.1" + }, + "peerDependencies": { + "assemblyscript": "^0.18.31" + } + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "node_modules/ts-mixer": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-5.4.1.tgz", + "integrity": "sha512-Zo9HgPCtNouDgJ+LGtrzVOjSg8+7WGQktIKLwAfaNrlOK1mWGlz1ejsAF/YqUEqAGjUTeB5fEg8gH9Aui6w9xA==" + } + }, + "dependencies": { + "@assemblyscript/loader": { + "version": "0.19.7", + "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.19.7.tgz", + "integrity": "sha512-/BdvOMHnfW3uIHFEgDPRbmA+BoQjuYpjKIZgI3p4sG8Jxgy9UuQW7cQUFx7dRlFUk/SBVCoB8N7412NgKyctqg==" + }, + "@suborbital/suborbital": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@suborbital/suborbital/-/suborbital-0.10.0.tgz", + "integrity": "sha512-Ggy58dYcqT3pBTvr6nOFuMyLKgsHFjdbz/B/UjPi0tHOQ3n91g0lwOcsIGq3Rlu01FW7ppseSAKkPe2zhvP67w==", + "requires": { + "@assemblyscript/loader": "^0.19" + } + }, + "assemblyscript": { + "version": "0.19.7", + "resolved": "https://registry.npmjs.org/assemblyscript/-/assemblyscript-0.19.7.tgz", + "integrity": "sha512-0Vs27D9yIqxbwAx/zp2CNMCgR/Aa2cQlMZO+ctW30rhQ0/UOS/J9PkFXRxUVNMYj0xwEMT/Cq6l9flXO2ZJFcQ==", + "dev": true, + "requires": { + "binaryen": "101.0.0-nightly.20210703", + "long": "^4.0.0" + } + }, + "binaryen": { + "version": "101.0.0-nightly.20210703", + "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-101.0.0-nightly.20210703.tgz", + "integrity": "sha512-7iuRy/aSsd8BlO0ZOG4GqDI8z66UhazD10eeC3i14SOhZJ+MLC3I+BX5ydr3nk9g5j2PDGndxuYvm0gy+VJHYg==", + "dev": true + }, + "json-as": { + "version": "git+ssh://git@github.com/aspkg/as-json.git#f5b00039ed9cd0b95f21bec0c85abf5cdbb32e3b", + "from": "json-as@github:aspkg/as-json", + "requires": { + "visitor-as": "^0.6.0" + }, + "dependencies": { + "assemblyscript": { + "version": "0.18.32", + "resolved": "https://registry.npmjs.org/assemblyscript/-/assemblyscript-0.18.32.tgz", + "integrity": "sha512-Py6zremwGhO3nSoI/VxyVUzTZfNhTjzNzFDaUdG4JhPJHeG+FzVlEoNCrw4bE5nPc7F+P2DJ8tZQCqIt15ceKw==", + "peer": true, + "requires": { + "binaryen": "100.0.0-nightly.20210413", + "long": "^4.0.0" + } + }, + "binaryen": { + "version": "100.0.0-nightly.20210413", + "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-100.0.0-nightly.20210413.tgz", + "integrity": "sha512-EeGLIxQmJS0xnYl+SH34mNBqVMoixKd9nsE7S7z+CtS9A4eoWn3Qjav+XElgunUgXIHAI5yLnYT2TUGnLX2f1w==", + "peer": true + }, + "visitor-as": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/visitor-as/-/visitor-as-0.6.0.tgz", + "integrity": "sha512-4WcnwCLXWjhNkwJj9gSqh46sdIv9CyIvnSuwr61OOfrGCtN2mKcW5KE828OeEr1rYjEy0Z/CIdPBJKJRLsUgDA==", + "requires": { + "lodash.clonedeep": "^4.5.0", + "ts-mixer": "^5.4.1" + } + } + } + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "ts-mixer": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-5.4.1.tgz", + "integrity": "sha512-Zo9HgPCtNouDgJ+LGtrzVOjSg8+7WGQktIKLwAfaNrlOK1mWGlz1ejsAF/YqUEqAGjUTeB5fEg8gH9Aui6w9xA==" + } + } +} diff --git a/rwasm/testdata/as-json/package.json b/rwasm/testdata/as-json/package.json new file mode 100755 index 00000000..7242b6aa --- /dev/null +++ b/rwasm/testdata/as-json/package.json @@ -0,0 +1,20 @@ +{ + "name": "as-json", + "version": "1.0.0", + "description": "", + "main": "src/index.ts", + "scripts": { + "test": "node tests", + "asbuild": "asc src/index.ts --transform ./node_modules/json-as/transform --target release --use abort=src/index/abort" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "assemblyscript": "^0.19" + }, + "dependencies": { + "@assemblyscript/loader": "^0.19", + "@suborbital/suborbital": "^0.10.0", + "json-as": "github:aspkg/as-json" + } +} diff --git a/rwasm/testdata/as-json/src/index.ts b/rwasm/testdata/as-json/src/index.ts new file mode 100755 index 00000000..38f997ab --- /dev/null +++ b/rwasm/testdata/as-json/src/index.ts @@ -0,0 +1,33 @@ +// DO NOT EDIT; generated file + +import { return_result, return_abort, toFFI, fromFFI, getIdent, setIdent } from "@suborbital/suborbital"; +import { run } from "./lib" + +export function run_e(ptr: usize, size: i32, ident: i32): void { + // set the current ident for other API methods to use + setIdent(ident) + + // read the memory that was passed as input + var inBuffer = fromFFI(ptr, size) + + // execute the Runnable + let result = run(inBuffer) + + // return the result to the host + return_result(changetype(result), result.byteLength, getIdent()) +} + +export function allocate(size: i32): usize { + return heap.alloc(size) +} + +export function deallocate(ptr: i32, _: i32): void { + heap.free(ptr) +} + +function abort(message: string | null, fileName: string | null, lineNumber: u32, columnNumber: u32): void { + let msgFFI = toFFI(String.UTF8.encode(message ? message : "")) + let fileFFI = toFFI(String.UTF8.encode(fileName ? fileName : "")) + + return_abort(msgFFI.ptr, msgFFI.size, fileFFI.ptr, fileFFI.size, lineNumber, columnNumber, getIdent()) +} \ No newline at end of file diff --git a/rwasm/testdata/as-json/src/lib.ts b/rwasm/testdata/as-json/src/lib.ts new file mode 100755 index 00000000..efd3471a --- /dev/null +++ b/rwasm/testdata/as-json/src/lib.ts @@ -0,0 +1,45 @@ +import { logInfo } from "@suborbital/suborbital" +import { JSON } from "json-as" + +// @ts-ignore +@json +class JSONSchema { + firstName: string + lastName: string + age: i32 + meta: Meta + tags: Array +} + +// @ts-ignore +@json +class Meta { + country: string + province: string + isAwesome: boolean +} + +export function run(_: ArrayBuffer): ArrayBuffer { + + const data: JSONSchema = { + firstName: 'Connor', + lastName: 'Hicks', + age: 26, + meta: { + country: "Canada", + province: "Ontario", + isAwesome: true, + }, + tags: ["hello", "world"] + } + + const stringified = JSON.stringify(data) + + logInfo(stringified) + + const parsed = JSON.parse(stringified) + + const stringifiedAgain = JSON.stringify(parsed) + + return String.UTF8.encode(stringifiedAgain) +} \ No newline at end of file diff --git a/rwasm/testdata/as-json/src/tsconfig.json b/rwasm/testdata/as-json/src/tsconfig.json new file mode 100755 index 00000000..e28fcf25 --- /dev/null +++ b/rwasm/testdata/as-json/src/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "assemblyscript/std/assembly.json", + "include": [ + "./**/*.ts" + ] +} \ No newline at end of file diff --git a/rwasm/testdata/rs-graphql/rs-graphql.wasm b/rwasm/testdata/rs-graphql/rs-graphql.wasm index 4ab6ba47..d3351ef9 100755 Binary files a/rwasm/testdata/rs-graphql/rs-graphql.wasm and b/rwasm/testdata/rs-graphql/rs-graphql.wasm differ diff --git a/rwasm/testdata/rs-graphql/src/lib.rs b/rwasm/testdata/rs-graphql/src/lib.rs index 881c0dcb..06570a95 100755 --- a/rwasm/testdata/rs-graphql/src/lib.rs +++ b/rwasm/testdata/rs-graphql/src/lib.rs @@ -6,19 +6,19 @@ use suborbital::util; struct RsGraqhql{} impl Runnable for RsGraqhql { - fn run(&self, input: Vec) -> Result, RunErr> { - let in_string = String::from_utf8(input).unwrap(); - - match query("https://api.rawkode.dev", "{ allProfiles { forename, surname } }") { + fn run(&self, _: Vec) -> Result, RunErr> { + let result = match query("https://api.github.com/graphql", "{ repository (owner: \"suborbital\", name: \"reactr\") { name, nameWithOwner }}") { Ok(response) => { - info(util::to_string(response).as_str()) + info(util::to_string(response.clone()).as_str()); + response } Err(e) => { - error(e.message.as_str()) + error(e.message.as_str()); + return Err(RunErr::new(1, e.message.as_str())) } - } + }; - Ok(String::from(format!("hello {}", in_string)).as_bytes().to_vec()) + Ok(result) } } diff --git a/rwasm/wasmtest/assemblyscript_test.go b/rwasm/wasmtest/assemblyscript_test.go index f6e15af8..1bd27b60 100644 --- a/rwasm/wasmtest/assemblyscript_test.go +++ b/rwasm/wasmtest/assemblyscript_test.go @@ -3,10 +3,12 @@ package wasmtest import ( "encoding/json" "fmt" + "os" "testing" "github.com/google/uuid" "github.com/pkg/errors" + "github.com/suborbital/reactr/rcap" "github.com/suborbital/reactr/request" "github.com/suborbital/reactr/rt" "github.com/suborbital/reactr/rwasm" @@ -48,11 +50,11 @@ func TestASFetch(t *testing.T) { } } -func TestASGraphql(t *testing.T) { +func TestASJSON(t *testing.T) { r := rt.New() // test a WASM module that is loaded directly instead of through the bundle - doWasm := r.Register("as-graphql", rwasm.NewRunner("../testdata/as-graphql/as-graphql.wasm")) + doWasm := r.Register("as-json", rwasm.NewRunner("../testdata/as-json/as-json.wasm")) res, err := doWasm("").Then() if err != nil { @@ -60,7 +62,39 @@ func TestASGraphql(t *testing.T) { return } - if string(res.([]byte)) != `{"data":{"allProfiles":[{"forename":"David","surname":"McKay"}]}}` { + if string(res.([]byte)) != `{"firstName":"Connor","lastName":"Hicks","age":26,"meta":{"country":"Canada","province":"Ontario","isAwesome":true},"tags":["hello","world"]}` { + t.Error("as-json failed, got:", string(res.([]byte))) + } +} + +func TestASGraphql(t *testing.T) { + // bail out if GitHub auth is not set up (i.e. in Travis) + if _, ok := os.LookupEnv("GITHUB_TOKEN"); !ok { + return + } + + r := rt.New() + + caps := r.DefaultCaps() + caps.Auth = rcap.DefaultAuthProvider(&rcap.AuthProviderConfig{ + Headers: map[string]rcap.AuthHeader{ + "api.github.com": { + HeaderType: "bearer", + Value: "env(GITHUB_TOKEN)", + }, + }, + }) + + // test a WASM module that is loaded directly instead of through the bundle + r.RegisterWithCaps("as-graphql", rwasm.NewRunner("../testdata/as-graphql/as-graphql.wasm"), caps) + + res, err := r.Do(rt.NewJob("as-graphql", nil)).Then() + if err != nil { + t.Error(errors.Wrap(err, "failed to Then")) + return + } + + if string(res.([]byte)) != `{"data":{"repository":{"name":"reactr","nameWithOwner":"suborbital/reactr"}}}` { t.Error("as-graphql failed, got:", string(res.([]byte))) } } diff --git a/rwasm/wasmtest/rust_test.go b/rwasm/wasmtest/rust_test.go index 7a41fa73..e6657a33 100644 --- a/rwasm/wasmtest/rust_test.go +++ b/rwasm/wasmtest/rust_test.go @@ -3,10 +3,12 @@ package wasmtest import ( "encoding/json" "fmt" + "os" "testing" "github.com/google/uuid" "github.com/pkg/errors" + "github.com/suborbital/reactr/rcap" "github.com/suborbital/reactr/request" "github.com/suborbital/reactr/rt" "github.com/suborbital/reactr/rwasm" @@ -34,16 +36,34 @@ func TestWasmRunnerWithFetch(t *testing.T) { } func TestGraphQLRunner(t *testing.T) { + // bail out if GitHub auth is not set up (i.e. in Travis) + if _, ok := os.LookupEnv("GITHUB_TOKEN"); !ok { + return + } + r := rt.New() - // test a WASM module that is loaded directly instead of through the bundle - doWasm := r.Register("wasm", rwasm.NewRunner("../testdata/rs-graphql/rs-graphql.wasm")) + caps := r.DefaultCaps() + caps.Auth = rcap.DefaultAuthProvider(&rcap.AuthProviderConfig{ + Headers: map[string]rcap.AuthHeader{ + "api.github.com": { + HeaderType: "bearer", + Value: "env(GITHUB_TOKEN)", + }, + }, + }) + + r.RegisterWithCaps("rs-graphql", rwasm.NewRunner("../testdata/rs-graphql/rs-graphql.wasm"), caps) - _, err := doWasm("").Then() + res, err := r.Do(rt.NewJob("rs-graphql", nil)).Then() if err != nil { t.Error(errors.Wrap(err, "failed to Then")) return } + + if string(res.([]byte)) != `{"data":{"repository":{"name":"reactr","nameWithOwner":"suborbital/reactr"}}}` { + t.Error("as-graphql failed, got:", string(res.([]byte))) + } } func TestWasmRunnerReturnError(t *testing.T) {