Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
665a090
feat: start .validate function on client
cbrzn Oct 14, 2022
3a74fce
chore: start tests of validate function
cbrzn Oct 14, 2022
b613eb6
feat: implemented abi validation & start exhaustive testing in valida…
cbrzn Oct 14, 2022
09d4d78
chore: recursive tests implemented
cbrzn Oct 17, 2022
e21b830
feat: abis are compared if flag is passed
cbrzn Oct 17, 2022
0692dd3
chore: house cleaning a bit
cbrzn Oct 17, 2022
3b908f2
chore: abi validation test implemented
cbrzn Oct 17, 2022
d1b087e
chore: fix typos on comments
cbrzn Oct 17, 2022
6a1b7f2
chore: client validate mvp finished
cbrzn Oct 17, 2022
5bc179d
chore: removed test environment from test & use getManifest from wrap…
cbrzn Oct 18, 2022
8335c12
chore: move compare function outside of formats & return multiple uri…
cbrzn Oct 18, 2022
6a71e89
chore: remove local lint changes from test file
cbrzn Oct 19, 2022
c9dfe6d
chore: remove unused deploy manifest from test case & clean up wrap m…
cbrzn Oct 19, 2022
80b49fd
chore: merge conflicts & fix client validate test
cbrzn Oct 25, 2022
d0ad8fe
chore: fix lint
cbrzn Oct 25, 2022
07bcb1d
merge origin-dev
dOrgJelli Nov 15, 2022
c1fc4e5
Merge branch 'origin-dev' into feat/client-validate
cbrzn Nov 22, 2022
32b60e7
chore: use static resolver on validate tests & update with latest cha…
cbrzn Nov 22, 2022
240cf20
Merge branch 'origin-dev' into feat/client-validate
cbrzn Nov 28, 2022
7d64be3
Merge branch 'feat/client-validate' of github.com:polywrap/toolchain …
cbrzn Nov 28, 2022
cb24354
chore(client): compareSignature now expects an object instead of a ty…
cbrzn Nov 28, 2022
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
85 changes: 85 additions & 0 deletions packages/js/client/src/PolywrapClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
InterfaceImplementations,
InvokeOptions,
InvokerOptions,
ValidateOptions,
QueryOptions,
SubscribeOptions,
Subscription,
Expand All @@ -31,10 +32,12 @@ import { buildCleanUriHistory } from "@polywrap/uri-resolvers-js";
import { msgpackEncode, msgpackDecode } from "@polywrap/msgpack-js";
import {
DeserializeManifestOptions,
ImportedModuleDefinition,
WrapManifest,
} from "@polywrap/wrap-manifest-types-js";
import { Tracer, TracerConfig, TracingLevel } from "@polywrap/tracing-js";
import { Result, ResultErr, ResultOk } from "@polywrap/result";
import { compareSignature } from "@polywrap/wrap-manifest-types-js";
import { ClientConfigBuilder } from "@polywrap/client-config-builder-js";

export class PolywrapClient implements CoreClient {
Expand Down Expand Up @@ -694,6 +697,88 @@ export class PolywrapClient implements CoreClient {
}
}

@Tracer.traceMethod("PolywrapClient: validateConfig")
public async validate<TUri extends Uri | string>(
uri: TUri,
options: ValidateOptions
): Promise<Result<true, Error>> {
const wrapper = await this.loadWrapper(Uri.from(uri));
if (!wrapper.ok) {
return ResultErr(new Error(wrapper.error?.message));
}

const { abi } = await wrapper.value.getManifest();
const importedModules: ImportedModuleDefinition[] =
abi.importedModuleTypes || [];

const importUri = (importedModuleType: ImportedModuleDefinition) => {
return this.tryResolveUri({ uri: importedModuleType.uri });
};
const resolvedModules = await Promise.all(importedModules.map(importUri));
const modulesNotFound = resolvedModules.filter(({ ok }) => !ok) as {
error: Error;
}[];

if (modulesNotFound.length) {
const missingModules = modulesNotFound.map(({ error }) => {
const uriIndex = error?.message.indexOf("\n");
return error?.message.substring(0, uriIndex);
});
const error = new Error(JSON.stringify(missingModules));
return ResultErr(error);
}

if (options.abi) {
for (const importedModule of importedModules) {
const importedModuleManifest = await this.getManifest(
importedModule.uri
);
if (!importedModuleManifest.ok) {
return ResultErr(importedModuleManifest.error);
}
const importedMethods =
importedModuleManifest.value.abi.moduleType?.methods || [];

const expectedMethods = importedModules.find(
({ uri }) => importedModule.uri === uri
);

const errorMessage = `ABI from Uri: ${importedModule.uri} is not compatible with Uri: ${uri}`;
for (const [i, _] of Object.keys(importedMethods).entries()) {
const importedMethod = importedMethods[i];

if (expectedMethods?.methods && expectedMethods?.methods.length < i) {
const expectedMethod = expectedMethods?.methods[i];
const areEqual = compareSignature(importedMethod, expectedMethod);

if (!areEqual) return ResultErr(new Error(errorMessage));
} else {
return ResultErr(new Error(errorMessage));
}
}
}
}

if (options.recursive) {
const validateImportedModules = importedModules.map(({ uri }) =>
this.validate(uri, options)
);
const resolverUris = await Promise.all(validateImportedModules);
const invalidUris = resolverUris.filter(({ ok }) => !ok) as {
error: Error;
}[];
if (invalidUris.length) {
const missingUris = invalidUris.map(({ error }) => {
const uriIndex = error?.message.indexOf("\n");
return error?.message.substring(0, uriIndex);
});
const error = new Error(JSON.stringify(missingUris));
return ResultErr(error);
}
}
return ResultOk(true);
}

private buildConfigFromPolywrapClientConfig(
config?: PolywrapClientConfig
): PolywrapCoreClientConfig<Uri> {
Expand Down
113 changes: 110 additions & 3 deletions packages/js/client/src/__tests__/core/sanity.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { coreInterfaceUris } from "@polywrap/core-js";
import { Uri } from "../..";
import { PolywrapClient } from "../../PolywrapClient";
import {
Uri,
PolywrapClient, PolywrapCoreClientConfig
} from "../..";
import fs from "fs";

import { coreInterfaceUris, IUriPackage, IUriRedirect } from "@polywrap/core-js";
import { buildWrapper } from "@polywrap/test-env-js";
import { ResultErr } from "@polywrap/result";
import { StaticResolver, UriResolverLike } from "@polywrap/uri-resolvers-js";
import { WasmPackage } from "@polywrap/wasm-js";

jest.setTimeout(200000);

Expand All @@ -26,4 +34,103 @@ describe("sanity", () => {
},
]);
});

test("validate requested uri is available", async () => {
const fooPath = `${__dirname}/../utils/validate/wrapper-a`;
const greetingPath = `${__dirname}/../utils/validate/wrapper-b`;
const modifiedFooPath = `${__dirname}/../utils/validate/wrapper-c`
const fooUri = `ens/foo.eth`;
const greetingUri = `ens/greeting.eth`;
const modifiedFooUri = `ens/foo-modified.eth`;

const getPackage = async (name: string) => {
const manifest = await fs.promises.readFile(
`${__dirname}/../utils/validate/${name}/build/wrap.info`
);

const wasmModule = await fs.promises.readFile(
`${__dirname}/../utils/validate/${name}/build/wrap.wasm`
);
return WasmPackage.from(manifest, wasmModule)
}

let config: unknown = {
resolver: {
tryResolveUri: (_a: unknown, _b: unknown, _c: unknown) => {
return Promise.resolve(ResultErr())
}
},
interfaces: undefined,
envs: undefined
}

await buildWrapper(fooPath);
let client = new PolywrapClient(config as PolywrapCoreClientConfig, { noDefaults: true });
let result = await client.validate(fooUri, {});
expect(result.ok).toBeFalsy();
let resultError = (result as { error: Error }).error;
expect(resultError).toBeTruthy();
expect(resultError.message).toContain("Error resolving URI");

let fooPackage: IUriPackage<string> = {
uri: fooUri,
package: await getPackage("wrapper-a")
}

let resolvers: UriResolverLike[] = [ fooPackage ]
let staticResolver = StaticResolver.from(resolvers)

config = {
resolver: staticResolver
};

client = new PolywrapClient(config as PolywrapCoreClientConfig, { noDefaults: true });
result = await client.validate(fooUri, {});

expect(result.ok).toBeTruthy();

result = await client.validate(greetingUri, {
recursive: true
})
resultError = (result as { error: Error }).error;
expect(result.ok).toBeFalsy();
expect(resultError).toBeTruthy();
expect(resultError.message).toContain("Error resolving URI");

await buildWrapper(greetingPath);

let modifiedFooWrapper: IUriPackage<string> = {
uri: greetingUri,
package: await getPackage("wrapper-b")
};
resolvers.push(modifiedFooWrapper);
staticResolver = StaticResolver.from(resolvers);

(config as Record<string, unknown>).resolver = staticResolver;
client = new PolywrapClient(config as PolywrapCoreClientConfig, { noDefaults: true });

result = await client.validate(greetingUri, {
recursive: true
})

expect(result.ok).toBeTruthy()

await buildWrapper(modifiedFooPath);
let redirectUri: IUriRedirect<string> = {
from: fooUri,
to: modifiedFooUri
};
resolvers.push(redirectUri);

staticResolver = StaticResolver.from(resolvers);

(config as Record<string, unknown>).resolver = staticResolver;
client = new PolywrapClient(config as PolywrapCoreClientConfig, { noDefaults: true });

result = await client.validate(greetingUri, {
abi: true
})

expect(result.ok).toBeFalsy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
Comment thread
cbrzn marked this conversation as resolved.
"name": "wrapper-a",
"private": true,
"dependencies": {
"@polywrap/wasm-as": "0.9.2",
"assemblyscript": "0.19.23"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
format: 0.2.0
project:
name: WrapperA
type: wasm/assemblyscript
source:
schema: ./schema.graphql
module: ./src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
type Module {
foo(bar: String!): String!
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {
Args_foo
} from "./wrap";

export function foo(args: Args_foo): String {
return args.bar
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "wrapper-b",
"private": true,
"dependencies": {
"@polywrap/wasm-as": "0.9.2",
"assemblyscript": "0.19.23"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
format: 0.2.0
project:
name: WrapperB
type: wasm/assemblyscript
source:
schema: ./schema.graphql
module: ./src/index.ts
import_abis:
- uri: "ens/foo.eth"
abi: "../wrapper-a/build/wrap.info"
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#import { Module } into Foo from "ens/foo.eth"

type Module {
greeting(message: String!): String!
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {
Args_greeting,
Foo_Module
} from "./wrap";

export function greeting(args: Args_greeting): String {
return Foo_Module.foo({ bar: args.message }).unwrap();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "wrapper-c",
"private": true,
"dependencies": {
"@polywrap/wasm-as": "0.9.2",
"assemblyscript": "0.19.23"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
format: 0.2.0
project:
name: WrapperC
type: wasm/assemblyscript
source:
schema: ./schema.graphql
module: ./src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
type Module {
foo(bar: Int!): String!
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {
Args_foo
} from "./wrap";

export function foo(args: Args_foo): String {
return args.bar.toString()
}
10 changes: 10 additions & 0 deletions packages/js/core/src/types/CoreClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ export interface GetImplementationsOptions {
resolutionContext?: IUriResolutionContext;
}

export interface ValidateOptions {
abi?: boolean;
recursive?: boolean;
}

export interface CoreClient
extends Invoker,
QueryHandler,
Expand Down Expand Up @@ -60,4 +65,9 @@ export interface CoreClient
uri: TUri,
options: GetImplementationsOptions
): Promise<Result<TUri[], Error>>;

validate<TUri extends Uri | string>(
uri: TUri,
options?: ValidateOptions
): Promise<Result<true, Error>>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,4 @@ export type AnyWrapAbi =
export { migrateWrapManifest } from "./migrate";
export { deserializeWrapManifest } from "./deserialize";
export { serializeWrapManifest } from "./serialize";
export { validateWrapManifest } from "./validate";
export { validateWrapManifest } from "./validate";
25 changes: 25 additions & 0 deletions packages/js/manifests/wrap/src/compareSignature.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {
MethodDefinition, PropertyDefinition,
} from ".";

export default (
importedMethod: MethodDefinition,
expectedMethod: MethodDefinition
): boolean => {
if (expectedMethod.name === importedMethod.name) {
return false
}

if (expectedMethod.arguments) {
const expectedArgs = expectedMethod.arguments.some(({type, name}, index) => {
const importedArgs = importedMethod.arguments as PropertyDefinition[]
const { name: importedName, type: importedType } = importedArgs[index]
return importedName === name && importedType === type
})

if (!expectedArgs) {
return false
}
}
return true
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,4 @@ export const latestWrapAbiVersion = "0.1";
export { migrateWrapManifest } from "./migrate";
export { deserializeWrapManifest } from "./deserialize";
export { serializeWrapManifest } from "./serialize";
export { validateWrapManifest } from "./validate";
export { validateWrapManifest } from "./validate";
Loading