Skip to content

Commit

Permalink
feat: OneClient throws validation error
Browse files Browse the repository at this point in the history
  • Loading branch information
freaz committed Aug 17, 2023
1 parent f25f6c6 commit 6c5a460
Show file tree
Hide file tree
Showing 13 changed files with 109 additions and 20 deletions.
11 changes: 10 additions & 1 deletion core/core/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
//! Use usecase to request some behaviour for perform:
//! - CORE_PERFORM_PANIC
//! - CORE_PERFORM_TRUE
//! - CORE_PERFORM_INPUT_VALIDATION_ERROR

use sf_std::unstable::{
perform::{set_perform_output_result_in, PerformInput},
exception::{PerformException, PerformExceptionErrorCode},
perform::{set_perform_output_exception_in, set_perform_output_result_in, PerformInput},
HostValue,
};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
Expand Down Expand Up @@ -43,6 +45,13 @@ pub fn __export_oneclient_core_perform() {
"CORE_PERFORM_TRUE" => {
set_perform_output_result_in(HostValue::Bool(true), MessageExchangeFfi)
}
"CORE_PERFORM_INPUT_VALIDATION_ERROR" => set_perform_output_exception_in(
PerformException {
error_code: PerformExceptionErrorCode::InputValidationError,
message: "Test validation error".to_string(),
},
MessageExchangeFfi,
),
_ => panic!("Unknown usecase: {}", perform_input.usecase),
};
}
8 changes: 4 additions & 4 deletions core/core/src/sf_core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ pub struct OneClientCore {
}
impl OneClientCore {
const MAP_STDLIB_JS: &str = include_str!("../assets/js/map_std.js");
const SECURITY_VALUES_JSON_SCHEMA: &str =
include_str!("../assets/schemas/security_values.json");

// TODO: Use thiserror and define specific errors
pub fn new(config: &CoreConfiguration) -> anyhow::Result<Self> {
Expand All @@ -67,10 +69,8 @@ impl OneClientCore {
provider_cache: DocumentCache::new(config.cache_duration, config.registry_url.clone()),
map_cache: DocumentCache::new(config.cache_duration, config.registry_url.clone()),
security_validator: JsonSchemaValidator::new(
&serde_json::Value::from_str(include_str!(
"../assets/schemas/security_values.json"
))
.expect("Valid JSON"),
&serde_json::Value::from_str(&OneClientCore::SECURITY_VALUES_JSON_SCHEMA)
.expect("Valid JSON"),
)
.expect("Valid JSON Schema for security values exists"),
mapstd_config: MapStdImplConfig {
Expand Down
4 changes: 3 additions & 1 deletion examples/node_js/index.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createServer } from 'http';
import { OneClient, PerformError, UnexpectedError } from '../../host/javascript/node/index.js';
import { OneClient, PerformError, UnexpectedError, ValidationError } from '../../host/javascript/node/index.js';

async function startLocalhostServer() {
const server = createServer((req, res) => {
Expand Down Expand Up @@ -45,6 +45,8 @@ try {
} catch (e) {
if (e instanceof PerformError) {
console.log('ERROR RESULT:', e.errorResult);
} else if (e instanceof ValidationError) {
console.error('VALIDATION ERROR:', e.message);
} else if (e instanceof UnexpectedError) {
console.error('ERROR:', e);
} else {
Expand Down
4 changes: 3 additions & 1 deletion examples/python/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import sys
from http.server import BaseHTTPRequestHandler, HTTPServer

from one_sdk import OneClient, PerformError, UnexpectedError
from one_sdk import OneClient, PerformError, ValidationError, UnexpectedError

class MyServer(BaseHTTPRequestHandler):
def do_GET(self):
Expand Down Expand Up @@ -41,6 +41,8 @@ def do_GET(self):
print(f"RESULT: {r}")
except PerformError as e:
print(f"ERROR RESULT: {e.error_result}")
except ValidationError as e:
print(f"INVALID INPUT: {e.message}", file = sys.stderr)
except UnexpectedError as e:
print(f"ERROR:", e, file = sys.stderr)
finally:
Expand Down
5 changes: 5 additions & 0 deletions host/javascript/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import {
OneClient,
PerformError,
UnexpectedError,
ValidationError,
} from '@superfaceai/one-sdk/node';

const client = new OneClient();
Expand All @@ -103,9 +104,13 @@ try {
},
}
);

console.log('RESULT:', result);
} catch (e) {
if (e instanceof PerformError) {
console.log('ERROR RESULT:', e.errorResult);
} else if (e instanceof ValidationError) {
console.error('VALIDATION ERROR:', e.message);
} else if (e instanceof UnexpectedError) {
console.error('ERROR:', e);
} else {
Expand Down
26 changes: 20 additions & 6 deletions host/javascript/src/common/app.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { createRequire } from 'module';
import { WASI } from 'wasi';

import { App } from './app.js';
import { UnexpectedError } from './error.js';
import { FileSystem, Persistence, Network, TextCoder, Timers, WasiContext } from './interfaces.js';
import { UnexpectedError, ValidationError } from './error.js';
import { FileSystem, Persistence, Network, TextCoder, Timers } from './interfaces.js';


class TestNetwork implements Network {
Expand Down Expand Up @@ -52,8 +52,8 @@ class TestTimers implements Timers {
}

class TestPersistence implements Persistence {
async persistMetrics(events: string[]): Promise<void> {}
async persistDeveloperDump(events: string[]): Promise<void> {}
async persistMetrics(events: string[]): Promise<void> { }
async persistDeveloperDump(events: string[]): Promise<void> { }
}

describe('App', () => {
Expand All @@ -73,7 +73,7 @@ describe('App', () => {
await readFile(createRequire(import.meta.url).resolve('../../assets/test-core-async.wasm'))
);

await app.init(new WASI());
await app.init(new WASI({ version: 'preview1' } as any));

handleMessage = jest.spyOn(app, 'handleMessage');
handleMessage.mockImplementation(async (message) => {
Expand Down Expand Up @@ -130,7 +130,7 @@ describe('App', () => {
);
} catch (e) { }

await app.init(new WASI());
await app.init(new WASI({ version: 'preview1' } as any));

const result = await app.perform(
'',
Expand All @@ -144,4 +144,18 @@ describe('App', () => {

expect(result).toBe(true);
});

test('invalid user input', async () => {
handleMessage.mockRestore();

await expect(app.perform(
'',
'',
'',
'CORE_PERFORM_INPUT_VALIDATION_ERROR',
null,
{},
{},
)).rejects.toThrowError(ValidationError);
});
});
12 changes: 8 additions & 4 deletions host/javascript/src/common/app.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Asyncify } from './asyncify.js';
import { HandleMap } from './handle_map.js';

import { PerformError, UnexpectedError, UninitializedError, WasiErrno, WasiError } from './error.js';
import { PerformError, UnexpectedError, UninitializedError, ValidationError, WasiErrno, WasiError } from './error.js';
import { AppContext, FileSystem, Network, TextCoder, Timers, WasiContext, Persistence } from './interfaces.js';
import { SecurityValuesMap } from './security.js';
import * as sf_host from './sf_host.js';
Expand Down Expand Up @@ -239,7 +239,7 @@ export class App implements AppContext {
}

/**
* @throws {PerformError | UnexpectedError}
* @throws {PerformError | ValidationError | UnexpectedError}
*/
public async perform(
profileUrl: string,
Expand Down Expand Up @@ -273,7 +273,7 @@ export class App implements AppContext {
}

public async handleMessage(message: any): Promise<any> {
switch (message['kind']) {
switch (message.kind) {
case 'perform-input':
return {
kind: 'ok',
Expand All @@ -295,7 +295,11 @@ export class App implements AppContext {
return { kind: 'ok' };

case 'perform-output-exception':
this.performState!.exception = new UnexpectedError(message.exception.error_code, message.exception.message);
if (message.exception.error_code === "InputValidationError") {
this.performState!.exception = new ValidationError(message.exception.message);
} else {
this.performState!.exception = new UnexpectedError(message.exception.error_code, message.exception.message);
}
return { kind: 'ok' };

case 'file-open': {
Expand Down
6 changes: 6 additions & 0 deletions host/javascript/src/common/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ export class PerformError extends BaseError {
}
}

export class ValidationError extends BaseError {
constructor(message: string) {
super(ValidationError.name, message);
}
}

export class UnexpectedError extends BaseError {
constructor(name: string, message: string) {
super(name, message);
Expand Down
2 changes: 2 additions & 0 deletions host/python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ try:
print(f"RESULT: {r}")
except PerformError as e:
print(f"ERROR RESULT: {e.error_result}")
except ValidationError as e:
print(f"INVALID INPUT: {e.message}", file = sys.stderr)
except UnexpectedError as e:
print(f"ERROR:", e, file = sys.stderr)
finally:
Expand Down
2 changes: 1 addition & 1 deletion host/python/src/one_sdk/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
from one_sdk.client import OneClient
from one_sdk.error import UnexpectedError, PerformError
from one_sdk.error import UnexpectedError, ValidationError, PerformError
7 changes: 5 additions & 2 deletions host/python/src/one_sdk/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from one_sdk.handle_map import HandleMap
from one_sdk.sf_host import Ptr, Size, link as sf_host_link
from one_sdk.error import HostError, ErrorCode, PerformError, UnexpectedError, UninitializedError, WasiError, WasiErrno
from one_sdk.error import HostError, ErrorCode, PerformError, ValidationError, UnexpectedError, UninitializedError, WasiError, WasiErrno
from one_sdk.platform import PythonFilesystem, PythonNetwork, PythonPersistence, DeferredHttpResponse, HttpResponse

# TODO: TypeAlias - needs 3.10
Expand Down Expand Up @@ -133,7 +133,10 @@ def handle_message(self, message: Any) -> Any:
self._perform_state.error = PerformError(message["error"])
return { "kind": "ok" }
elif message["kind"] == "perform-output-exception":
self._perform_state.exception = UnexpectedError(message["exception"]["error_code"], message["exception"]["message"])
if message["exception"]["error_code"] == "InputValidationError":
self._perform_state.exception = ValidationError(message["exception"]["message"])
else:
self._perform_state.exception = UnexpectedError(message["exception"]["error_code"], message["exception"]["message"])
return { "kind": "ok" }
elif message["kind"] == "file-open":
try:
Expand Down
4 changes: 4 additions & 0 deletions host/python/src/one_sdk/error.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ def __init__(self, error_result: Any):
super().__init__("PerformError", str(error_result))
self.error_result = error_result

class ValidationError(BaseError):
def __init__(self, message: str):
super().__init__("ValidationError", message)

class UnexpectedError(BaseError):
def __init__(self, name: str, message: str):
super().__init__(name, message)
Expand Down
38 changes: 38 additions & 0 deletions host/python/tests/test_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import os
import unittest

from one_sdk import ValidationError
from one_sdk.app import WasiApp
from one_sdk.platform import PythonFilesystem, PythonNetwork, PythonPersistence

class TestApp(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.app = WasiApp(
filesystem = PythonFilesystem,
network = PythonNetwork,
persistence = PythonPersistence
)

with open(os.path.abspath(os.path.join(__file__, "../../src/one_sdk/assets/test-core.wasm")), "rb") as file:
cls.app.load_core(file.read())
cls.app.init()

@classmethod
def tearDownClass(cls):
cls.app.destroy()

def test_invalid_user_input(self):
with self.assertRaises(ValidationError):
self.app.perform(
profile_url = '',
provider_url = '',
map_url = '',
usecase = 'CORE_PERFORM_INPUT_VALIDATION_ERROR',
input = {},
parameters = {},
security = {}
)

if __name__ == '__main__':
unittest.main()

0 comments on commit 6c5a460

Please sign in to comment.