-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement parser and typescript code generator (#5)
- Loading branch information
1 parent
e8e9e5b
commit cd0277c
Showing
38 changed files
with
4,805 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
name: 'CI-CD sails-js' | ||
|
||
on: | ||
pull_request: | ||
types: [opened, synchronize, reopened, labeled] | ||
branches: [master] | ||
push: | ||
branches: [master] | ||
paths: | ||
- js/** | ||
workflow_dispatch: | ||
|
||
jobs: | ||
test: | ||
if: github.event_name == 'pull_request' | ||
|
||
runs-on: ubuntu-22.04 | ||
env: | ||
RUSTUP_HOME: /tmp/rustup_home | ||
steps: | ||
- name: Cancel previous workflow runs | ||
uses: styfle/cancel-workflow-action@0.4.0 | ||
with: | ||
access_token: ${{ github.token }} | ||
|
||
- name: Checkout | ||
uses: actions/checkout@v3 | ||
|
||
- name: "Install: NodeJS 18.x" | ||
uses: actions/setup-node@v3 | ||
with: | ||
node-version: 18.x | ||
|
||
- name: "Install: pkg dependencies" | ||
working-directory: js | ||
run: yarn install | ||
|
||
- name: "Build: sails-js" | ||
working-directory: js | ||
run: yarn build | ||
|
||
- name: "Test: run" | ||
working-directory: js | ||
run: yarn test | ||
|
||
publish-to-npm: | ||
if: github.event_name == 'push' | ||
|
||
runs-on: ubuntu-22.04 | ||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v3 | ||
|
||
- name: Use node 18.x | ||
uses: actions/setup-node@v3 | ||
with: | ||
node-version: 18.x | ||
|
||
- name: Check package version | ||
uses: EndBug/version-check@v1 | ||
id: check | ||
with: | ||
file-name: js/package.json | ||
file-url: https://unpkg.com/sails-js@latest/package.json | ||
static-checking: localIsNew | ||
|
||
- name: Install dependencies | ||
if: steps.check.outputs.changed == 'true' | ||
working-directory: js | ||
run: yarn install | ||
|
||
- name: Build sails-js | ||
if: steps.check.outputs.changed == 'true' | ||
working-directory: js | ||
run: yarn build | ||
|
||
- name: Publish | ||
if: steps.check.outputs.changed == 'true' | ||
working-directory: js | ||
run: | | ||
export token=$(printenv $(printenv GITHUB_ACTOR)) | ||
npm config set //registry.npmjs.org/:_authToken=$token | ||
npm publish | ||
env: | ||
osipov-mit: ${{ secrets.SAILS_JS_NPM_TOKEN }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,10 @@ | ||
node_modules/ | ||
js/lib/ | ||
|
||
.DS_Store | ||
|
||
.vscode | ||
target/ | ||
.binpath | ||
#.vscode | ||
.vscode | ||
#.log |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
## Installation | ||
The package can be installed as either a global dependency or a package dependency. | ||
|
||
```bash | ||
npm install -g sails-js | ||
``` | ||
or | ||
```bash | ||
npm install sails-js | ||
``` | ||
|
||
## Usage | ||
|
||
### CLI | ||
- Generate typescript code from the IDL file | ||
```bash | ||
sails-js generate path/to/sails.idl -o path/to/out/dir | ||
``` | ||
This command will generate 2 files to the specified directory. | ||
|
||
- Parse IDL file and print the result | ||
```bash | ||
sails-js parse-and-print path/to/sails.idl | ||
``` | ||
|
||
- Parse IDL file and save the result to a json file | ||
```bash | ||
sails-js parse-into-file path/to/sails.idl path/to/out.json | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
node_modules/ | ||
yarn.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"name": "example", | ||
"version": "1.0.0", | ||
"main": "index.js", | ||
"license": "MIT", | ||
"dependencies": { | ||
"@gear-js/api": "^0.35.2", | ||
"@polkadot/api": "^10.11.1", | ||
"@polkadot/types": "^10.11.1" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
type Alias = u32; | ||
|
||
type OptionAlias = opt u8; | ||
|
||
type ResultAlias = result (u8, string) | ||
|
||
type VecAlias = vec u8; | ||
|
||
type ThisThatSvcAppTupleStruct = struct { | ||
bool, | ||
}; | ||
|
||
type ThisThatSvcAppDoThatParam = struct { | ||
p1: u32, | ||
p2: str, | ||
p3: ThisThatSvcAppManyVariants, | ||
}; | ||
|
||
type ThisThatSvcAppManyVariants = enum { | ||
One, | ||
Two: u32, | ||
Three: opt u32, | ||
Four: struct { a: u32, b: opt u16 }, | ||
Five: struct { str, u32 }, | ||
Six: struct { u32 }, | ||
}; | ||
|
||
service { | ||
DoThis : (p1: u32, p2: str, p3: struct { opt str, u8 }, p4: ThisThatSvcAppTupleStruct) -> struct { str, u32 }; | ||
DoThat : (param: ThisThatSvcAppDoThatParam) -> result (struct { str, u32 }, struct { str }); | ||
query This : () -> u32; | ||
query That : () -> result (str, str); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import { GearApi } from '@gear-js/api'; | ||
import { Transaction } from './transaction.js'; | ||
import { IKeyringPair } from '@polkadot/types/types'; | ||
|
||
export type Alias = bigint; | ||
|
||
export type OptionAlias = null | number; | ||
|
||
export type ResultAlias = { ok: number } | { err: string }; | ||
|
||
export type VecAlias = Array<number>; | ||
|
||
export type ThisThatSvcAppTupleStruct = [boolean]; | ||
|
||
export interface ThisThatSvcAppDoThatParam { | ||
p1: bigint; | ||
p2: string; | ||
p3: ThisThatSvcAppManyVariants; | ||
} | ||
|
||
export type ThisThatSvcAppManyVariants = | ||
| { One: null } | ||
| { Two: bigint } | ||
| { Three: null | bigint } | ||
| { Four: { a: bigint; b: null | number } } | ||
| { Five: [string, bigint] } | ||
| { Six: [bigint] }; | ||
|
||
export class Service extends Transaction { | ||
constructor(api: GearApi, public programId: `0x${string}`) { | ||
const types: Record<string, any> = { | ||
Alias: "u32", | ||
OptionAlias: "Option<u8>", | ||
ResultAlias: "Result<u8, string>", | ||
VecAlias: "Vec<u8>", | ||
ThisThatSvcAppTupleStruct: "(bool)", | ||
ThisThatSvcAppDoThatParam: {"p1":"u32","p2":"str","p3":"ThisThatSvcAppManyVariants"}, | ||
ThisThatSvcAppManyVariants: {"_enum":{"One":null,"Two":"u32","Three":"Option<u32>","Four":{"a":"u32","b":"Option<u16>"},"Five":"(str, u32)","Six":"(u32)"}}, | ||
} | ||
super(api, types); | ||
} | ||
|
||
public async doThis(p1: bigint, p2: string, p3: [null | string, number], p4: ThisThatSvcAppTupleStruct, account: `0x${string}` | IKeyringPair): Promise<[string, bigint]> { | ||
const payload = [ | ||
...this.registry.createType('String', 'DoThis/').toU8a(), | ||
...this.registry.createType('u32', p1).toU8a(), | ||
...this.registry.createType('str', p2).toU8a(), | ||
...this.registry.createType('(Option<str>, u8)', p3).toU8a(), | ||
...this.registry.createType('ThisThatSvcAppTupleStruct', p4).toU8a(), | ||
]; | ||
const replyPayloadBytes = await this.submitMsgAndWaitForReply( | ||
this.programId, | ||
payload, | ||
account, | ||
); | ||
const result = this.registry.createType('(str, u32)', replyPayloadBytes); | ||
return result.toJSON() as [string, bigint]; | ||
} | ||
|
||
public async doThat(param: ThisThatSvcAppDoThatParam, account: `0x${string}` | IKeyringPair): Promise<{ ok: [string, bigint] } | { err: [string] }> { | ||
const payload = [ | ||
...this.registry.createType('String', 'DoThat/').toU8a(), | ||
...this.registry.createType('ThisThatSvcAppDoThatParam', param).toU8a(), | ||
]; | ||
const replyPayloadBytes = await this.submitMsgAndWaitForReply( | ||
this.programId, | ||
payload, | ||
account, | ||
); | ||
const result = this.registry.createType('Result<(str, u32), (str)>', replyPayloadBytes); | ||
return result.toJSON() as { ok: [string, bigint] } | { err: [string] }; | ||
} | ||
|
||
public async this(): Promise<bigint> { | ||
const payload = this.registry.createType('String', 'This/').toU8a(); | ||
const stateBytes = await this.api.programState.read({ programId: this.programId, payload}); | ||
const result = this.registry.createType('u32', stateBytes); | ||
return result.toBigInt() as bigint; | ||
} | ||
|
||
public async that(): Promise<{ ok: string } | { err: string }> { | ||
const payload = this.registry.createType('String', 'That/').toU8a(); | ||
const stateBytes = await this.api.programState.read({ programId: this.programId, payload}); | ||
const result = this.registry.createType('Result<str, str>', stateBytes); | ||
return result.toJSON() as { ok: string } | { err: string }; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import { GearApi, HexString, MessageQueuedData, UserMessageSentData, decodeAddress } from '@gear-js/api'; | ||
import { SubmittableExtrinsic } from '@polkadot/api/types'; | ||
import { TypeRegistry } from '@polkadot/types'; | ||
import { IKeyringPair, ISubmittableResult } from '@polkadot/types/types'; | ||
import { Registry } from '@polkadot/types-codec/types/registry'; | ||
import { ReplaySubject } from 'rxjs'; | ||
|
||
export class Transaction { | ||
protected registry: Registry; | ||
|
||
constructor(protected api: GearApi, types: Record<string, any>) { | ||
this.registry = new TypeRegistry(); | ||
|
||
this.registry.setKnownTypes({ types }); | ||
this.registry.register(types); | ||
} | ||
|
||
private sendTx( | ||
tx: SubmittableExtrinsic<'promise', ISubmittableResult>, | ||
account: IKeyringPair | string, | ||
): Promise<string> { | ||
return new Promise((resolve, reject) => | ||
tx | ||
.signAndSend(account, ({ events, status }) => { | ||
if (status.isInBlock) { | ||
let msgId: string; | ||
|
||
events.forEach(({ event }) => { | ||
const { method, section, data } = event; | ||
if (method === 'MessageQueued' && section === 'Gear') { | ||
msgId = (data as MessageQueuedData).id.toHex(); | ||
} else if (section === 'System' && method === 'ExtrinsicSuccess') { | ||
resolve(msgId); | ||
} else if (section === 'System' && method === 'ExtrinsicFailed') { | ||
reject(this.api.getExtrinsicFailedError(event)); | ||
} | ||
}); | ||
} | ||
}) | ||
.catch((error) => { | ||
reject(error.message); | ||
}), | ||
); | ||
} | ||
|
||
private async listenToUserMessageSentEvents(from: string, to: string) { | ||
const subject = new ReplaySubject<UserMessageSentData>(5); | ||
const unsub = await this.api.gearEvents.subscribeToGearEvent('UserMessageSent', ({ data }) => { | ||
if (data.message.source.eq(from) && data.message.destination.eq(to)) { | ||
subject.next(data); | ||
} | ||
}); | ||
|
||
return { unsub, subject }; | ||
} | ||
|
||
private async waitForReply(subject: ReplaySubject<UserMessageSentData>, msgId: string): Promise<HexString> { | ||
return new Promise<HexString>((resolve, reject) => { | ||
subject.subscribe(({ message }) => { | ||
if (message.details.isSome) { | ||
if (message.details.unwrap().to.eq(msgId)) { | ||
if (!message.details.unwrap().code.isSuccess) { | ||
reject(message.payload.toString()); | ||
} else { | ||
resolve(message.payload.toHex()); | ||
} | ||
} | ||
} | ||
}); | ||
}); | ||
} | ||
|
||
protected async submitMsgAndWaitForReply( | ||
programId: HexString, | ||
payload: any, | ||
account: string | IKeyringPair, | ||
): Promise<HexString> { | ||
const addressHex = decodeAddress(typeof account === 'string' ? account : account.address); | ||
|
||
const gasLimit = await this.api.program.calculateGas.handle(addressHex, programId, payload, 0, false); | ||
|
||
const tx = this.api.message.send({ | ||
destination: programId, | ||
payload, | ||
gasLimit: gasLimit.min_limit, | ||
}); | ||
|
||
const { unsub, subject } = await this.listenToUserMessageSentEvents(addressHex, programId); | ||
|
||
const msgId = await this.sendTx(tx, account); | ||
|
||
const replyPayload = await this.waitForReply(subject, msgId); | ||
|
||
unsub(); | ||
|
||
return replyPayload; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import type { Config } from '@jest/types'; | ||
|
||
const config: Config.InitialOptions = { | ||
clearMocks: true, | ||
coverageProvider: 'v8', | ||
testEnvironment: 'node', | ||
verbose: true, | ||
preset: 'ts-jest/presets/js-with-babel', | ||
extensionsToTreatAsEsm: ['.ts'], | ||
moduleNameMapper: { | ||
'^(\\.{1,2}/.*)\\.js$': '$1', | ||
}, | ||
transform: { | ||
'^.+\\.tsx?$': ['ts-jest', { useESM: true }], | ||
}, | ||
}; | ||
|
||
export default config; |
Oops, something went wrong.