Skip to content
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
73 changes: 73 additions & 0 deletions packages/ast/__tests__/utils/__snapshots__/safe-names.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`reserved schema names execute class escapes reserved identifiers 1`] = `
"export class ReservedClient implements ReservedInstance {
client: ISigningCosmWasmClient;
sender: string;
contractAddress: string;
constructor(client: ISigningCosmWasmClient, sender: string, contractAddress: string) {
this.client = client;
this.sender = sender;
this.contractAddress = contractAddress;
this._constructor = this._constructor.bind(this);
}
_constructor = async ({
class: _class,
default: _default,
delete: _delete
}: {
class: string;
default: string;
delete: boolean;
}, fee_: number | StdFee | "auto" = "auto", memo_?: string, funds_?: Coin[]): Promise<any> => {
return await this.client.execute(this.sender, this.contractAddress, {
constructor: {
class: _class,
default: _default,
delete: _delete
}
}, fee_, memo_, funds_);
};
}"
`;

exports[`reserved schema names execute interface escapes reserved identifiers 1`] = `
"export interface ReservedInstance {
contractAddress: string;
sender: string;
_constructor: (params: {
class: string;
default: string;
delete: boolean;
}, fee_?: number | StdFee | "auto", memo_?: string, funds_?: Coin[]) => Promise<any>;
}"
`;

exports[`reserved schema names query class escapes reserved identifiers 1`] = `
"export class ReservedQueryClient implements ReservedReadOnlyInstance {
client: ICosmWasmClient;
contractAddress: string;
constructor(client: ICosmWasmClient, contractAddress: string) {
this.client = client;
this.contractAddress = contractAddress;
this._constructor = this._constructor.bind(this);
}
_constructor = async ({
class: _class,
default: _default,
delete: _delete
}: {
class: string;
default: string;
delete: boolean;
}): Promise<ConstructorResponse> => {
return this.client.queryContractSmart(this.contractAddress, {
constructor: {
class: _class,
default: _default,
delete: _delete
}
});
};
}"
`;
151 changes: 151 additions & 0 deletions packages/ast/__tests__/utils/safe-names.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { ExecuteMsg, QueryMsg } from '@cosmwasm/ts-codegen-types';

import {
createExecuteClass,
createExecuteInterface,
createQueryClass,
} from '../../src';
import { getMessageProperties } from '../../src/utils';
import { camelMethodName, camelVarName, varName } from '../../src/utils/names';
import { expectCode, makeContext } from '../../test-utils';

describe('name helpers', () => {
it('escapes reserved words for bindings', () => {
expect(varName('class')).toBe('_class');
expect(varName('default')).toBe('_default');
expect(varName('delete')).toBe('_delete');
expect(varName('owner')).toBe('owner');
});

it('escapes unsafe member names', () => {
expect(camelMethodName('constructor')).toBe('_constructor');
expect(camelMethodName('transfer_nft')).toBe('transferNft');
expect(camelVarName('default')).toBe('_default');
expect(camelVarName('token_id')).toBe('tokenId');
});
});

describe('operation name collisions', () => {
const collidingMsg: ExecuteMsg = {
$schema: 'http://json-schema.org/draft-07/schema#',
title: 'ExecuteMsg',
oneOf: [
{
type: 'object',
required: ['transfer_nft'],
properties: {
transfer_nft: {
type: 'object',
required: ['recipient', 'token_id'],
properties: {
recipient: { type: 'string' },
token_id: { type: 'string' },
},
},
},
additionalProperties: false,
},
{
type: 'object',
required: ['transferNft'],
properties: {
transferNft: {
type: 'object',
required: ['recipient', 'token_id'],
properties: {
recipient: { type: 'string' },
token_id: { type: 'string' },
},
},
},
additionalProperties: false,
},
],
};

it('throws on ambiguous normalized operation names', () => {
expect(() => getMessageProperties(collidingMsg)).toThrow(
/Operation name collision/
);
});
});

describe('reserved schema names', () => {
const queryMsg: QueryMsg = {
$schema: 'http://json-schema.org/draft-07/schema#',
title: 'QueryMsg',
oneOf: [
{
type: 'object',
required: ['constructor'],
properties: {
constructor: {
type: 'object',
required: ['class', 'default', 'delete'],
properties: {
class: { type: 'string' },
default: { type: 'string' },
delete: { type: 'boolean' },
},
},
},
additionalProperties: false,
},
],
};

const executeMsg: ExecuteMsg = {
$schema: 'http://json-schema.org/draft-07/schema#',
title: 'ExecuteMsg',
oneOf: [
{
type: 'object',
required: ['constructor'],
properties: {
constructor: {
type: 'object',
required: ['class', 'default', 'delete'],
properties: {
class: { type: 'string' },
default: { type: 'string' },
delete: { type: 'boolean' },
},
},
},
additionalProperties: false,
},
],
};

it('query class escapes reserved identifiers', () => {
const ctx = makeContext(queryMsg);
expectCode(
createQueryClass(
ctx,
'ReservedQueryClient',
'ReservedReadOnlyInstance',
queryMsg
)
);
});

it('execute class escapes reserved identifiers', () => {
const ctx = makeContext(executeMsg);
expectCode(
createExecuteClass(
ctx,
'ReservedClient',
'ReservedInstance',
null,
executeMsg
)
);
});

it('execute interface escapes reserved identifiers', () => {
const ctx = makeContext(executeMsg);
expectCode(
createExecuteInterface(ctx, 'ReservedInstance', null, executeMsg)
);
});
});
22 changes: 12 additions & 10 deletions packages/ast/src/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { RenderContext } from '../context';
import {
arrowFunctionExpression,
bindMethod,
camelMethodName,
camelVarName,
classDeclaration,
classProperty,
FIXED_EXECUTE_PARAMS,
Expand Down Expand Up @@ -47,7 +49,7 @@ export const createWasmQueryMethod = (
jsonschema: any
) => {
const underscoreName = Object.keys(jsonschema.properties)[0];
const methodName = camel(underscoreName);
const methodName = camelMethodName(underscoreName);
const responseType = getResponseType(context, underscoreName);

const param = createTypedObjectParams(
Expand Down Expand Up @@ -113,7 +115,7 @@ export const createQueryClass = (
.map((method) => Object.keys(method.properties)?.[0])
.filter(Boolean);

const bindings = propertyNames.map(camel).map(bindMethod);
const bindings = propertyNames.map(camelMethodName).map(bindMethod);

const methods = getMessageProperties(queryMsg).map((schema) => {
return createWasmQueryMethod(context, schema);
Expand Down Expand Up @@ -205,9 +207,9 @@ export const getWasmMethodArgs = (
const args = keys.map((prop) => {
return t.objectProperty(
t.identifier(prop),
t.identifier(camel(prop)),
t.identifier(camelVarName(prop)),
false,
prop === camel(prop)
prop === camelVarName(prop)
);
});

Expand All @@ -222,7 +224,7 @@ export const createWasmExecMethod = (
context.addUtil('Coin');

const underscoreName = Object.keys(jsonschema.properties)[0];
const methodName = camel(underscoreName);
const methodName = camelMethodName(underscoreName);
const param = createTypedObjectParams(
context,
jsonschema.properties[underscoreName]
Expand Down Expand Up @@ -301,7 +303,7 @@ export const createExecuteClass = (
.map((method) => Object.keys(method.properties)?.[0])
.filter(Boolean);

const bindings = propertyNames.map(camel).map(bindMethod);
const bindings = propertyNames.map(camelMethodName).map(bindMethod);

const methods = getMessageProperties(execMsg).map((schema) => {
return createWasmExecMethod(context, schema);
Expand Down Expand Up @@ -426,7 +428,7 @@ export const createExecuteInterface = (
) => {
const methods = getMessageProperties(execMsg).map((jsonschema) => {
const underscoreName = Object.keys(jsonschema.properties)[0];
const methodName = camel(underscoreName);
const methodName = camelMethodName(underscoreName);
return createPropertyFunctionWithObjectParamsForExec(
context,
methodName,
Expand Down Expand Up @@ -469,7 +471,7 @@ export const createPropertyFunctionWithObjectParams = (
responseType: string,
jsonschema: JSONSchema
) => {
const obj = createTypedObjectParams(context, jsonschema);
const obj = createTypedObjectParams(context, jsonschema, true, true);

const func = {
type: 'TSFunctionType',
Expand All @@ -494,7 +496,7 @@ export const createPropertyFunctionWithObjectParamsForExec = (
) => {
context.addUtil('Coin');

const obj = createTypedObjectParams(context, jsonschema);
const obj = createTypedObjectParams(context, jsonschema, true, true);

const func = {
type: 'TSFunctionType',
Expand All @@ -518,7 +520,7 @@ export const createQueryInterface = (
) => {
const methods = getMessageProperties(queryMsg).map((jsonschema) => {
const underscoreName = Object.keys(jsonschema.properties)[0];
const methodName = camel(underscoreName);
const methodName = camelMethodName(underscoreName);
const responseType = getResponseType(context, underscoreName);
return createPropertyFunctionWithObjectParams(
context,
Expand Down
4 changes: 2 additions & 2 deletions packages/ast/src/message-builder/message-builder.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import * as t from '@babel/types';
import { Expression } from '@babel/types';
import { ExecuteMsg, QueryMsg } from '@cosmwasm/ts-codegen-types';
import { camel } from 'case';

import { getWasmMethodArgs } from '../client/client';
import { RenderContext } from '../context';
import {
abstractClassDeclaration,
arrowFunctionExpression,
camelMethodName,
getMessageProperties,
} from '../utils';
import { createTypedObjectParams } from '../utils/types';
Expand Down Expand Up @@ -62,7 +62,7 @@ const createStaticExecMethodMessageBuilder = (
msgTitle: string
) => {
const underscoreName = Object.keys(jsonschema.properties)[0];
const methodName = camel(underscoreName);
const methodName = camelMethodName(underscoreName);
const param = createTypedObjectParams(
context,
jsonschema.properties[underscoreName]
Expand Down
10 changes: 5 additions & 5 deletions packages/ast/src/message-composer/message-composer.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import * as t from '@babel/types';
import { Expression } from '@babel/types';
import { ExecuteMsg, JSONSchema } from '@cosmwasm/ts-codegen-types';
import { camel } from 'case';

import { getWasmMethodArgs } from '../client/client';
import { RenderContext } from '../context';
import {
arrowFunctionExpression,
bindMethod,
camelMethodName,
classDeclaration,
classProperty,
getMessageProperties,
Expand All @@ -26,7 +26,7 @@ const createWasmExecMethodMessageComposer = (
context.addUtil('toUtf8');

const underscoreName = Object.keys(jsonschema.properties)[0];
const methodName = camel(underscoreName);
const methodName = camelMethodName(underscoreName);
const param = createTypedObjectParams(
context,
jsonschema.properties[underscoreName]
Expand Down Expand Up @@ -133,7 +133,7 @@ export const createMessageComposerClass = (
.map((method) => Object.keys(method.properties)?.[0])
.filter(Boolean);

const bindings = propertyNames.map(camel).map(bindMethod);
const bindings = propertyNames.map(camelMethodName).map(bindMethod);

const methods = getMessageProperties(execMsg).map((schema) => {
return createWasmExecMethodMessageComposer(context, schema);
Expand Down Expand Up @@ -205,7 +205,7 @@ export const createMessageComposerInterface = (
) => {
const methods = getMessageProperties(execMsg).map((jsonschema) => {
const underscoreName = Object.keys(jsonschema.properties)[0];
const methodName = camel(underscoreName);
const methodName = camelMethodName(underscoreName);
return createPropertyFunctionWithObjectParamsForMessageComposer(
context,
methodName,
Expand Down Expand Up @@ -246,7 +246,7 @@ const createPropertyFunctionWithObjectParamsForMessageComposer = (
responseType: string,
jsonschema: JSONSchema
) => {
const obj = createTypedObjectParams(context, jsonschema);
const obj = createTypedObjectParams(context, jsonschema, true, true);
const fixedParams = [OPTIONAL_FUNDS_PARAM];
const func = {
type: 'TSFunctionType',
Expand Down
Loading
Loading