From f98a8bdfc52cd6fc568e67b38b3dbc67ea93249d Mon Sep 17 00:00:00 2001 From: adairrr <32375605+adairrr@users.noreply.github.com> Date: Fri, 14 Apr 2023 09:22:49 +0200 Subject: [PATCH 1/2] Generate parameters for tuple types --- __fixtures__/basic/ownership.json | 154 ++++++++++++++++++ packages/wasm-ast-types/src/client/client.ts | 53 +++--- .../ts-client.issue-101.spec.ts.snap | 47 ++++++ .../ts-client.vectis.spec.ts.snap | 16 +- .../client/test/ts-client.issue-101.spec.ts | 37 +++++ .../message-composer.spec.ts.snap | 60 +++++++ .../message-composer/message-composer.spec.ts | 61 ++++--- .../src/message-composer/message-composer.ts | 18 +- .../__snapshots__/msg-builder.spec.ts.snap | 21 +++ .../src/msg-builder/msg-builder.spec.ts | 10 +- .../src/msg-builder/msg-builder.ts | 60 ++++--- .../__snapshots__/react-query.spec.ts.snap | 45 +++++ .../src/react-query/react-query.spec.ts | 18 +- packages/wasm-ast-types/src/utils/types.ts | 15 +- 14 files changed, 528 insertions(+), 87 deletions(-) create mode 100644 __fixtures__/basic/ownership.json create mode 100644 packages/wasm-ast-types/src/client/test/__snapshots__/ts-client.issue-101.spec.ts.snap create mode 100644 packages/wasm-ast-types/src/client/test/ts-client.issue-101.spec.ts diff --git a/__fixtures__/basic/ownership.json b/__fixtures__/basic/ownership.json new file mode 100644 index 00000000..c97252fd --- /dev/null +++ b/__fixtures__/basic/ownership.json @@ -0,0 +1,154 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "description": "Ownership Execute msg", + "oneOf": [ + { + "description": "Sets a new Factory", + "type": "object", + "required": [ + "set_factory" + ], + "properties": { + "set_factory": { + "type": "object", + "required": [ + "new_factory" + ], + "properties": { + "new_factory": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Update the contract's ownership. The `action` to be provided can be either to propose transferring ownership to an account, accept a pending ownership transfer, or renounce the ownership permanently.", + "type": "object", + "required": [ + "update_ownership" + ], + "properties": { + "update_ownership": { + "$ref": "#/definitions/Action" + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Action": { + "description": "Actions that can be taken to alter the contract's ownership", + "oneOf": [ + { + "description": "Propose to transfer the contract's ownership to another account, optionally with an expiry time.\n\nCan only be called by the contract's current owner.\n\nAny existing pending ownership transfer is overwritten.", + "type": "object", + "required": [ + "transfer_ownership" + ], + "properties": { + "transfer_ownership": { + "type": "object", + "required": [ + "new_owner" + ], + "properties": { + "expiry": { + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "new_owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Accept the pending ownership transfer.\n\nCan only be called by the pending owner.", + "type": "string", + "enum": [ + "accept_ownership" + ] + }, + { + "description": "Give up the contract's ownership and the possibility of appointing a new owner.\n\nCan only be invoked by the contract's current owner.\n\nAny existing pending ownership transfer is canceled.", + "type": "string", + "enum": [ + "renounce_ownership" + ] + } + ] + }, + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + } diff --git a/packages/wasm-ast-types/src/client/client.ts b/packages/wasm-ast-types/src/client/client.ts index 49b64984..a91dfe09 100644 --- a/packages/wasm-ast-types/src/client/client.ts +++ b/packages/wasm-ast-types/src/client/client.ts @@ -1,23 +1,19 @@ import * as t from '@babel/types'; -import { camel, pascal } from 'case'; +import { camel } from 'case'; import { + arrowFunctionExpression, bindMethod, - typedIdentifier, - promiseTypeAnnotation, classDeclaration, classProperty, - arrowFunctionExpression, - getMessageProperties -} from '../utils' + getMessageProperties, + promiseTypeAnnotation, + typedIdentifier +} from '../utils'; -import { - QueryMsg, - ExecuteMsg -} from '../types'; +import { ExecuteMsg, JSONSchema, QueryMsg } from '../types'; -import { getPropertyType, getType, createTypedObjectParams, getResponseType } from '../utils/types'; +import { createTypedObjectParams, getPropertyType, getResponseType, getType } from '../utils/types'; import { RenderContext } from '../context'; -import { JSONSchema } from '../types'; import { identifier, propertySignature } from '../utils/babel'; export const CONSTANT_EXEC_PARAMS = [ @@ -89,7 +85,7 @@ export const createWasmQueryMethod = ( const methodName = camel(underscoreName); const responseType = getResponseType(context, underscoreName); - const obj = createTypedObjectParams( + const param = createTypedObjectParams( context, jsonschema.properties[underscoreName] ); @@ -99,13 +95,14 @@ export const createWasmQueryMethod = ( jsonschema.properties[underscoreName] ); - const actionArg = - t.objectProperty(t.identifier(underscoreName), t.objectExpression(args)); + const msgAction = t.identifier(underscoreName); + // If the param is an identifier, we can just use it as is + const msgActionValue = param?.type === 'Identifier' ? t.identifier(param.name) : t.objectExpression(args) return t.classProperty( t.identifier(methodName), arrowFunctionExpression( - obj ? [obj] : [], + param ? [param] : [], t.blockStatement( [ t.returnStatement( @@ -120,7 +117,7 @@ export const createWasmQueryMethod = ( [ t.memberExpression(t.thisExpression(), t.identifier('contractAddress')), t.objectExpression([ - actionArg + t.objectProperty(msgAction, msgActionValue) ]) ] ) @@ -237,9 +234,15 @@ export const getWasmMethodArgs = ( // only 1 degree $ref-lookup if (!keys.length && jsonschema.$ref) { const obj = context.refLookup(jsonschema.$ref); + // properties if (obj) { keys = Object.keys(obj.properties ?? {}) } + + // tuple struct or otherwise, use the name of the reference + if (!keys.length && obj?.oneOf) { +// TODO????? ADAIR + } } const args = keys.map(prop => { @@ -265,7 +268,7 @@ export const createWasmExecMethod = ( const underscoreName = Object.keys(jsonschema.properties)[0]; const methodName = camel(underscoreName); - const obj = createTypedObjectParams( + const param = createTypedObjectParams( context, jsonschema.properties[underscoreName] ); @@ -274,12 +277,16 @@ export const createWasmExecMethod = ( jsonschema.properties[underscoreName] ); + const msgAction = t.identifier(underscoreName); + // If the param is an identifier, we can just use it as is + const msgActionValue = param?.type === 'Identifier' ? t.identifier(param.name) : t.objectExpression(args) + return t.classProperty( t.identifier(methodName), arrowFunctionExpression( - obj ? [ + param ? [ // props - obj, + param, ...CONSTANT_EXEC_PARAMS ] : CONSTANT_EXEC_PARAMS, t.blockStatement( @@ -306,10 +313,8 @@ export const createWasmExecMethod = ( t.objectExpression( [ t.objectProperty( - t.identifier(underscoreName), - t.objectExpression([ - ...args - ]) + msgAction, + msgActionValue ) ] diff --git a/packages/wasm-ast-types/src/client/test/__snapshots__/ts-client.issue-101.spec.ts.snap b/packages/wasm-ast-types/src/client/test/__snapshots__/ts-client.issue-101.spec.ts.snap new file mode 100644 index 00000000..17159f90 --- /dev/null +++ b/packages/wasm-ast-types/src/client/test/__snapshots__/ts-client.issue-101.spec.ts.snap @@ -0,0 +1,47 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`execute interfaces no extends 1`] = ` +"export interface OwnershipInstance { + contractAddress: string; + sender: string; + setFactory: ({ + newFactory + }: { + newFactory: string; + }, fee?: number | StdFee | \\"auto\\", memo?: string, funds?: Coin[]) => Promise; + updateOwnership: (action: Action, fee?: number | StdFee | \\"auto\\", memo?: string, funds?: Coin[]) => Promise; +}" +`; + +exports[`ownership client with tuple 1`] = ` +"export class OwnershipClient implements OwnershipInstance { + client: SigningCosmWasmClient; + sender: string; + contractAddress: string; + + constructor(client: SigningCosmWasmClient, sender: string, contractAddress: string) { + this.client = client; + this.sender = sender; + this.contractAddress = contractAddress; + this.setFactory = this.setFactory.bind(this); + this.updateOwnership = this.updateOwnership.bind(this); + } + + setFactory = async ({ + newFactory + }: { + newFactory: string; + }, fee: number | StdFee | \\"auto\\" = \\"auto\\", memo?: string, funds?: Coin[]): Promise => { + return await this.client.execute(this.sender, this.contractAddress, { + set_factory: { + new_factory: newFactory + } + }, fee, memo, funds); + }; + updateOwnership = async (action: Action, fee: number | StdFee | \\"auto\\" = \\"auto\\", memo?: string, funds?: Coin[]): Promise => { + return await this.client.execute(this.sender, this.contractAddress, { + update_ownership: action + }, fee, memo, funds); + }; +}" +`; diff --git a/packages/wasm-ast-types/src/client/test/__snapshots__/ts-client.vectis.spec.ts.snap b/packages/wasm-ast-types/src/client/test/__snapshots__/ts-client.vectis.spec.ts.snap index ef9254e6..e2b809bf 100644 --- a/packages/wasm-ast-types/src/client/test/__snapshots__/ts-client.vectis.spec.ts.snap +++ b/packages/wasm-ast-types/src/client/test/__snapshots__/ts-client.vectis.spec.ts.snap @@ -193,9 +193,9 @@ exports[`query classes 1`] = ` this.wasm = this.wasm.bind(this); } - bank = async (): Promise => { + bank = async (bankMsg: BankMsg): Promise => { return this.client.queryContractSmart(this.contractAddress, { - bank: {} + bank: bankMsg }); }; custom = async (): Promise => { @@ -203,19 +203,19 @@ exports[`query classes 1`] = ` custom: {} }); }; - staking = async (): Promise => { + staking = async (stakingMsg: StakingMsg): Promise => { return this.client.queryContractSmart(this.contractAddress, { - staking: {} + staking: stakingMsg }); }; - distribution = async (): Promise => { + distribution = async (distributionMsg: DistributionMsg): Promise => { return this.client.queryContractSmart(this.contractAddress, { - distribution: {} + distribution: distributionMsg }); }; - wasm = async (): Promise => { + wasm = async (wasmMsg: WasmMsg): Promise => { return this.client.queryContractSmart(this.contractAddress, { - wasm: {} + wasm: wasmMsg }); }; }" diff --git a/packages/wasm-ast-types/src/client/test/ts-client.issue-101.spec.ts b/packages/wasm-ast-types/src/client/test/ts-client.issue-101.spec.ts new file mode 100644 index 00000000..71b4efa1 --- /dev/null +++ b/packages/wasm-ast-types/src/client/test/ts-client.issue-101.spec.ts @@ -0,0 +1,37 @@ +import { createExecuteClass, createExecuteInterface } from '../client'; +import { expectCode, makeContext } from '../../../test-utils'; +import ownership from '../../../../../__fixtures__/basic/ownership.json'; + + +// it('query classes', () => { +// const ctx = makeContext(cosmos_msg_for__empty); +// expectCode(createQueryClass( +// ctx, +// 'SG721QueryClient', +// 'SG721ReadOnlyInstance', +// cosmos_msg_for__empty +// )) +// }); + +it('execute interfaces no extends', () => { + const ctx = makeContext(ownership); + expectCode(createExecuteInterface( + ctx, + 'OwnershipInstance', + null, + ownership + )) +}); + +it('ownership client with tuple', () => { + const ctx = makeContext(ownership); + expectCode(createExecuteClass( + ctx, + 'OwnershipClient', + 'OwnershipInstance', + null, + ownership + )) +}); + + diff --git a/packages/wasm-ast-types/src/message-composer/__snapshots__/message-composer.spec.ts.snap b/packages/wasm-ast-types/src/message-composer/__snapshots__/message-composer.spec.ts.snap index 8973cd20..2fa4a2fb 100644 --- a/packages/wasm-ast-types/src/message-composer/__snapshots__/message-composer.spec.ts.snap +++ b/packages/wasm-ast-types/src/message-composer/__snapshots__/message-composer.spec.ts.snap @@ -269,3 +269,63 @@ exports[`execute classes 1`] = ` }; }" `; + +exports[`ownershipClass 1`] = ` +"export class OwnershipMessageComposer implements OwnershipMessage { + sender: string; + contractAddress: string; + + constructor(sender: string, contractAddress: string) { + this.sender = sender; + this.contractAddress = contractAddress; + this.setFactory = this.setFactory.bind(this); + this.updateOwnership = this.updateOwnership.bind(this); + } + + setFactory = ({ + newFactory + }: { + newFactory: string; + }, funds?: Coin[]): MsgExecuteContractEncodeObject => { + return { + typeUrl: \\"/cosmwasm.wasm.v1.MsgExecuteContract\\", + value: MsgExecuteContract.fromPartial({ + sender: this.sender, + contract: this.contractAddress, + msg: toUtf8(JSON.stringify({ + set_factory: { + new_factory: newFactory + } + })), + funds + }) + }; + }; + updateOwnership = (action: Action, funds?: Coin[]): MsgExecuteContractEncodeObject => { + return { + typeUrl: \\"/cosmwasm.wasm.v1.MsgExecuteContract\\", + value: MsgExecuteContract.fromPartial({ + sender: this.sender, + contract: this.contractAddress, + msg: toUtf8(JSON.stringify({ + update_ownership: action + })), + funds + }) + }; + }; +}" +`; + +exports[`ownershipInterface 1`] = ` +"export interface OwnershipMessage { + contractAddress: string; + sender: string; + setFactory: ({ + newFactory + }: { + newFactory: string; + }, funds?: Coin[]) => MsgExecuteContractEncodeObject; + updateOwnership: (action: Action, funds?: Coin[]) => MsgExecuteContractEncodeObject; +}" +`; diff --git a/packages/wasm-ast-types/src/message-composer/message-composer.spec.ts b/packages/wasm-ast-types/src/message-composer/message-composer.spec.ts index 5b840aaf..e8a8f62b 100644 --- a/packages/wasm-ast-types/src/message-composer/message-composer.spec.ts +++ b/packages/wasm-ast-types/src/message-composer/message-composer.spec.ts @@ -1,25 +1,46 @@ -import execute_msg from '../../../../__fixtures__/basic/execute_msg_for__empty.json'; +import execute_msg from "../../../../__fixtures__/basic/execute_msg_for__empty.json"; +import ownership from "../../../../__fixtures__/basic/ownership.json"; + import { - createMessageComposerClass, - createMessageComposerInterface -} from './message-composer' -import { expectCode, makeContext } from '../../test-utils'; + createMessageComposerClass, + createMessageComposerInterface, +} from "./message-composer"; +import { expectCode, makeContext } from "../../test-utils"; +import * as t from "@babel/types"; +import { createReactQueryMutationHooks } from "../react-query"; + +it("execute classes", () => { + const ctx = makeContext(execute_msg); + expectCode( + createMessageComposerClass( + ctx, + "SG721MessageComposer", + "SG721Message", + execute_msg + ) + ); +}); + +it("createMessageComposerInterface", () => { + const ctx = makeContext(execute_msg); + expectCode(createMessageComposerInterface(ctx, "SG721Message", execute_msg)); +}); -it('execute classes', () => { - const ctx = makeContext(execute_msg); - expectCode(createMessageComposerClass( - ctx, - 'SG721MessageComposer', - 'SG721Message', - execute_msg - )) +it("ownershipClass", () => { + const ctx = makeContext(ownership); + expectCode( + createMessageComposerClass( + ctx, + "OwnershipMessageComposer", + "OwnershipMessage", + ownership + ) + ); }); -it('createMessageComposerInterface', () => { - const ctx = makeContext(execute_msg); - expectCode(createMessageComposerInterface( - ctx, - 'SG721Message', - execute_msg - )) +it("ownershipInterface", () => { + const ownershipCtx = makeContext(ownership); + expectCode( + createMessageComposerInterface(ownershipCtx, "OwnershipMessage", ownership) + ); }); diff --git a/packages/wasm-ast-types/src/message-composer/message-composer.ts b/packages/wasm-ast-types/src/message-composer/message-composer.ts index 80fc35c3..255b3327 100644 --- a/packages/wasm-ast-types/src/message-composer/message-composer.ts +++ b/packages/wasm-ast-types/src/message-composer/message-composer.ts @@ -14,6 +14,7 @@ import { JSONSchema } from '../types'; import { RenderContext } from '../context'; import { identifier } from '../utils/babel'; import { getWasmMethodArgs } from '../client/client'; +import { Expression } from '@babel/types'; const createWasmExecMethodMessageComposer = ( context: RenderContext, @@ -27,12 +28,20 @@ const createWasmExecMethodMessageComposer = ( const underscoreName = Object.keys(jsonschema.properties)[0]; const methodName = camel(underscoreName); - const obj = createTypedObjectParams(context, jsonschema.properties[underscoreName]); + const param = createTypedObjectParams(context, jsonschema.properties[underscoreName]); const args = getWasmMethodArgs( context, jsonschema.properties[underscoreName] ); + // what the underscore named property in the message is assigned to + let actionValue: Expression + if (param?.type === 'Identifier') { + actionValue = t.identifier(param.name); + } else { + actionValue = t.objectExpression(args) + } + const constantParams = [ identifier('funds', t.tsTypeAnnotation( t.tsArrayType( @@ -46,9 +55,9 @@ const createWasmExecMethodMessageComposer = ( return t.classProperty( t.identifier(methodName), arrowFunctionExpression( - obj ? [ + param ? [ // props - obj, + param, ...constantParams ] : constantParams, t.blockStatement( @@ -95,10 +104,9 @@ const createWasmExecMethodMessageComposer = ( ), [ t.objectExpression( - [ t.objectProperty( - t.identifier(underscoreName), t.objectExpression(args) + t.identifier(underscoreName), actionValue ) ] diff --git a/packages/wasm-ast-types/src/msg-builder/__snapshots__/msg-builder.spec.ts.snap b/packages/wasm-ast-types/src/msg-builder/__snapshots__/msg-builder.spec.ts.snap index aafd28dd..4f1c022e 100644 --- a/packages/wasm-ast-types/src/msg-builder/__snapshots__/msg-builder.spec.ts.snap +++ b/packages/wasm-ast-types/src/msg-builder/__snapshots__/msg-builder.spec.ts.snap @@ -113,6 +113,27 @@ exports[`execute class 1`] = ` }" `; +exports[`ownership 1`] = ` +"export abstract class Ownership { + static setFactory = ({ + newFactory + }: CamelCasedProperties[\\"set_factory\\"]>): ExecuteMsg => { + return { + set_factory: ({ + new_factory: newFactory + } as const) + }; + }; + static updateOwnership = (action: Action): ExecuteMsg => { + return { + update_ownership: action + }; + }; +}" +`; + exports[`query class 1`] = ` "export abstract class SG721MsgBuilder { static ownerOf = ({ diff --git a/packages/wasm-ast-types/src/msg-builder/msg-builder.spec.ts b/packages/wasm-ast-types/src/msg-builder/msg-builder.spec.ts index 5b1c43d4..54dcee2d 100644 --- a/packages/wasm-ast-types/src/msg-builder/msg-builder.spec.ts +++ b/packages/wasm-ast-types/src/msg-builder/msg-builder.spec.ts @@ -1,9 +1,12 @@ import execute_msg from '../../../../__fixtures__/basic/execute_msg_for__empty.json'; import query_msg from '../../../../__fixtures__/basic/query_msg.json'; +import ownership from '../../../../__fixtures__/basic/ownership.json'; + import { createMsgBuilderClass, } from './msg-builder' import { expectCode, makeContext } from '../../test-utils'; +import { findExecuteMsg } from '@cosmwasm/ts-codegen/src'; it('execute class', () => { const ctx = makeContext(execute_msg); @@ -12,6 +15,11 @@ it('execute class', () => { it('query class', () => { - const ctx = makeContext(execute_msg); + const ctx = makeContext(query_msg); expectCode(createMsgBuilderClass(ctx, 'SG721MsgBuilder', query_msg)) }); + +it('ownership', () => { + const ctx = makeContext(ownership); + expectCode(createMsgBuilderClass(ctx, 'Ownership', ownership)) +}); diff --git a/packages/wasm-ast-types/src/msg-builder/msg-builder.ts b/packages/wasm-ast-types/src/msg-builder/msg-builder.ts index fec31547..df7fa8a4 100644 --- a/packages/wasm-ast-types/src/msg-builder/msg-builder.ts +++ b/packages/wasm-ast-types/src/msg-builder/msg-builder.ts @@ -1,16 +1,11 @@ -import * as t from "@babel/types"; -import { camel } from "case"; -import { - abstractClassDeclaration, - arrowFunctionExpression, - bindMethod, - classDeclaration, - getMessageProperties, -} from "../utils"; -import { ExecuteMsg, QueryMsg } from "../types"; -import { createTypedObjectParams } from "../utils/types"; -import { RenderContext } from "../context"; -import { getWasmMethodArgs } from "../client/client"; +import * as t from '@babel/types'; +import { camel } from 'case'; +import { abstractClassDeclaration, arrowFunctionExpression, getMessageProperties } from '../utils'; +import { ExecuteMsg, QueryMsg } from '../types'; +import { createTypedObjectParams } from '../utils/types'; +import { RenderContext } from '../context'; +import { getWasmMethodArgs } from '../client/client'; +import { Expression, Identifier, PatternLike, TSAsExpression } from '@babel/types'; export const createMsgBuilderClass = ( context: RenderContext, @@ -34,10 +29,10 @@ export const createMsgBuilderClass = ( function createExtractTypeAnnotation(underscoreName: string, msgTitle: string) { return t.tsTypeAnnotation( t.tsTypeReference( - t.identifier("CamelCasedProperties"), + t.identifier('CamelCasedProperties'), t.tsTypeParameterInstantiation([ t.tsIndexedAccessType( - t.tsTypeReference(t.identifier("Extract"), + t.tsTypeReference(t.identifier('Extract'), t.tsTypeParameterInstantiation([ t.tsTypeReference(t.identifier(msgTitle)), t.tsTypeLiteral([ @@ -62,7 +57,7 @@ const createStaticExecMethodMsgBuilder = ( ) => { const underscoreName = Object.keys(jsonschema.properties)[0]; const methodName = camel(underscoreName); - const obj = createTypedObjectParams( + const param = createTypedObjectParams( context, jsonschema.properties[underscoreName] ); @@ -71,17 +66,34 @@ const createStaticExecMethodMsgBuilder = ( jsonschema.properties[underscoreName] ); - if (obj) obj.typeAnnotation = createExtractTypeAnnotation(underscoreName, msgTitle) + // what the underscore named property in the message is assigned to + let actionValue: Expression + if (param?.type === 'Identifier') { + actionValue = t.identifier(param.name); + } else { + actionValue = t.tsAsExpression(t.objectExpression(args), t.tsTypeReference(t.identifier('const'))); + } + + + // TODO: this is a hack to get the type annotation to work + // all type annotations in the future should be the extracted and camelized type + if ( + param && + param.typeAnnotation.type === 'TSTypeAnnotation' && + param.typeAnnotation.typeAnnotation.type === 'TSTypeLiteral' + ) { + param.typeAnnotation = createExtractTypeAnnotation(underscoreName, msgTitle); + } return t.classProperty( t.identifier(methodName), arrowFunctionExpression( // params - obj + param ? [ - // props - obj, - ] + // props + param + ] : [], // body t.blockStatement([ @@ -89,10 +101,10 @@ const createStaticExecMethodMsgBuilder = ( t.objectExpression([ t.objectProperty( t.identifier(underscoreName), - t.tsAsExpression(t.objectExpression(args), t.tsTypeReference(t.identifier('const'))) - ), + actionValue + ) ]) - ), + ) ]), // return type t.tsTypeAnnotation(t.tsTypeReference(t.identifier(msgTitle))), diff --git a/packages/wasm-ast-types/src/react-query/__snapshots__/react-query.spec.ts.snap b/packages/wasm-ast-types/src/react-query/__snapshots__/react-query.spec.ts.snap index 276b8fe5..0da3f360 100644 --- a/packages/wasm-ast-types/src/react-query/__snapshots__/react-query.spec.ts.snap +++ b/packages/wasm-ast-types/src/react-query/__snapshots__/react-query.spec.ts.snap @@ -1312,3 +1312,48 @@ export function useSg721TransferNftMutation(options?: Omit client.transferNft(msg, fee, memo, funds), options); }" `; + +exports[`ownership 1`] = ` +"export interface OwnershipUpdateOwnershipMutation { + client: OwnershipClient; + msg: Action; + args?: { + fee?: number | StdFee | \\"auto\\"; + memo?: string; + funds?: Coin[]; + }; +} +export function useOwnershipUpdateOwnershipMutation(options?: Omit, \\"mutationFn\\">) { + return useMutation(({ + client, + msg, + args: { + fee, + memo, + funds + } = {} + }) => client.updateOwnership(msg, fee, memo, funds), options); +} +export interface OwnershipSetFactoryMutation { + client: OwnershipClient; + msg: { + newFactory: string; + }; + args?: { + fee?: number | StdFee | \\"auto\\"; + memo?: string; + funds?: Coin[]; + }; +} +export function useOwnershipSetFactoryMutation(options?: Omit, \\"mutationFn\\">) { + return useMutation(({ + client, + msg, + args: { + fee, + memo, + funds + } = {} + }) => client.setFactory(msg, fee, memo, funds), options); +}" +`; diff --git a/packages/wasm-ast-types/src/react-query/react-query.spec.ts b/packages/wasm-ast-types/src/react-query/react-query.spec.ts index 7198b8a2..c959bd73 100644 --- a/packages/wasm-ast-types/src/react-query/react-query.spec.ts +++ b/packages/wasm-ast-types/src/react-query/react-query.spec.ts @@ -1,13 +1,15 @@ import * as t from '@babel/types'; import query_msg from '../../../../__fixtures__/basic/query_msg.json'; import execute_msg from '../../../../__fixtures__/basic/execute_msg_for__empty.json'; -import { RenderContext } from '../context'; +import ownership from '../../../../__fixtures__/basic/ownership.json'; + import { createReactQueryHooks, createReactQueryMutationHooks, } from './react-query' import { expectCode, makeContext } from '../../test-utils'; +import { createMsgBuilderClass } from '../msg-builder'; const execCtx = makeContext(execute_msg); const queryCtx = makeContext(query_msg); @@ -90,3 +92,17 @@ it('createReactQueryHooks', () => { ))) }); +it('ownership', () => { + const ownershipCtx = makeContext(ownership); + expectCode(t.program( + createReactQueryMutationHooks( + { + context: ownershipCtx, + execMsg: ownership, + contractName: 'Ownership', + ExecuteClient: 'OwnershipClient', + } + ))) +}); + + diff --git a/packages/wasm-ast-types/src/utils/types.ts b/packages/wasm-ast-types/src/utils/types.ts index a552f753..8693107d 100644 --- a/packages/wasm-ast-types/src/utils/types.ts +++ b/packages/wasm-ast-types/src/utils/types.ts @@ -1,5 +1,5 @@ import * as t from '@babel/types'; -import { camel, pascal } from 'case'; +import { camel, pascal, snake } from 'case'; import { propertySignature } from './babel'; import { TSTypeAnnotation } from '@babel/types'; import { RenderContext } from '../context'; @@ -425,15 +425,22 @@ export const createTypedObjectParams = ( context: RenderContext, jsonschema: JSONSchema, camelize: boolean = true -): t.ObjectPattern => { +): (t.Identifier | t.Pattern | t.RestElement) => { const keys = Object.keys(jsonschema.properties ?? {}); if (!keys.length) { - // is there a ref? if (jsonschema.$ref) { const obj = context.refLookup(jsonschema.$ref); - if (obj) { + // If there is a oneOf, then we need to create a type for it + if (obj?.oneOf) { + // the actual type of the ref + const refType = jsonschema.$ref.split('/').pop(); + const refName = camel(refType); + const id = t.identifier(refName); + id.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier(refType))); + return id + } else if (obj) { return createTypedObjectParams( context, obj, From 85e69d3dd3bae5ebb50676bdba1fa35d44505cb8 Mon Sep 17 00:00:00 2001 From: adairrr <32375605+adairrr@users.noreply.github.com> Date: Fri, 14 Apr 2023 09:29:22 +0200 Subject: [PATCH 2/2] Add msg-builder readme --- README.md | 29 +++++++++++++++++++++++++++++ packages/ts-codegen/README.md | 26 ++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/README.md b/README.md index 045b7bb8..a51ac9f9 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,9 @@ codegen({ }, messageComposer: { enabled: false + }, + msgBuilder: { + enabled: false } } }).then(() => { @@ -252,6 +255,28 @@ cosmwasm-ts-codegen generate \ | ------------------------------ | ------------------------------------------------------------------- | | `messageComposer.enabled` | enable the messageComposer plugin | +### Msg Builder + +Generate raw message jsons for use in your application with the `msg-builder` command. + +[see example output code](https://gist.github.com/adairrr/b394e62beb9856b0351883f776650f26) + +#### Msg Builder via CLI + +```sh +cosmwasm-ts-codegen generate \ + --plugin msg-builder \ + --schema ./schema \ + --out ./ts \ + --name MyContractName +``` +#### Message Composer Options + +| option | description | +-------------| ------------------------------ | ------------------------------------------------------------------- | +| `msgBuilder.enabled` | enable the msgBilder plugin | + + ### Bundles The bundler will make a nice package of all your contracts. For example: @@ -389,6 +414,10 @@ https://gist.github.com/pyramation/a9520ccf131177b1841e02a97d7d3731 https://gist.github.com/pyramation/43320e8b952751a0bd5a77dbc5b601f4 +- `cosmwasm-ts-codegen generate --plugin msg-builder` + +https://gist.github.com/adairrr/b394e62beb9856b0351883f776650f26 + ### JSON Schema diff --git a/packages/ts-codegen/README.md b/packages/ts-codegen/README.md index 045b7bb8..4fc0e4b6 100644 --- a/packages/ts-codegen/README.md +++ b/packages/ts-codegen/README.md @@ -36,6 +36,7 @@ The quickest and easiest way to interact with CosmWasm Contracts. `@cosmwasm/ts- - [React Query](#react-query) - [Recoil](#recoil) - [Message Composer](#message-composer) + - [Msg Builder](#msg-builder) - [Bundles](#bundles) - [CLI Usage and Examples](#cli-usage-and-examples) - [Advanced Usage](#advanced-usage) @@ -119,6 +120,9 @@ codegen({ }, messageComposer: { enabled: false + }, + msgBuilder: { + enabled: false } } }).then(() => { @@ -252,6 +256,28 @@ cosmwasm-ts-codegen generate \ | ------------------------------ | ------------------------------------------------------------------- | | `messageComposer.enabled` | enable the messageComposer plugin | +### Msg Builder + +Generate raw message jsons for use in your application with the `msg-builder` command. + +[see example output code](https://gist.github.com/adairrr/b394e62beb9856b0351883f776650f26) + +#### Msg Builder via CLI + +```sh +cosmwasm-ts-codegen generate \ + --plugin msg-builder \ + --schema ./schema \ + --out ./ts \ + --name MyContractName +``` +#### Message Composer Options + +| option | description | +-------------| ------------------------------ | ------------------------------------------------------------------- | +| `msgBuilder.enabled` | enable the msgBilder plugin | + + ### Bundles The bundler will make a nice package of all your contracts. For example: