Skip to content

Commit

Permalink
Better support for complex types (#13)
Browse files Browse the repository at this point in the history
* Add tests for nested Array. Prettify.

* Add more tests around argument resolvers

* Fix regular expressions for arrays and dictionaries

* Update tests

* Prettify

* Bump version

* Update CHANGELOG
  • Loading branch information
Maksim Daunarovich authored Jul 26, 2021
1 parent a51bbf6 commit 57cfe79
Show file tree
Hide file tree
Showing 8 changed files with 1,052 additions and 225 deletions.
9 changes: 7 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
### 0.1.6 - 2021-07-26
- Fixed support for nested arrays
- Improved support for complex types
- Improved test coverage

### 0.1.5 - 2021-06-27
- Removed `flow-js-testing` dependency
- Refactored code generation for all types of interactions
Expand All @@ -6,10 +11,10 @@
- Updated `flow-generate` CLI. Added GitHub support and flags.

### 0.1.4 - 2021-06-11
- Update regexp matchers to properly catch script and transaction arguments
- Updated regexp matchers to properly catch script and transaction arguments

### 0.1.3 - 2021-06-10
- Export mapArgument, mapArguments and mapValuesToCode methods.
- Exported mapArgument, mapArguments and mapValuesToCode methods.

### 0.1.2 -- 2021-06-02
- Regenerated templates to remove unused import, which would trigger linter.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "flow-cadut",
"version": "0.1.5",
"version": "0.1.6",
"description": "Flow Cadence Template Utilities",
"author": "Maksim Daunarovich",
"license": "Apache-2.0",
Expand Down
41 changes: 36 additions & 5 deletions src/args.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,18 @@ export const splitArgs = (pair) => {

export const argType = (pair) => splitArgs(pair)[1];

export const getDictionaryTypes = (type) => type.replace(/[\s{}]/g, "").split(":");
export const getArrayType = (type) => type.replace(/[\s[\]]/g, "");
export const getDictionaryTypes = (type) => {
const match = /{(.*)}/.exec(type);
return match[1]
.split(/([^:]*):(.*)/)
.map((item) => item.replace(/\s/g, ""))
.filter((item) => item);
};

export const getArrayType = (type) => {
const match = /\[(.*)\]/.exec(type);
return match[1];
};

/**
* Reports missing arguments.
Expand Down Expand Up @@ -75,6 +85,25 @@ export const reportMissing = (itemType = "items", found, required, prefix = "")
}
};

export const resolveType = (type) => {
if (isComplexType(type)) {
switch (true) {
case isArray(type): {
const arrayType = getArrayType(type);
let finalType = t[arrayType];
if (isArray(arrayType)) {
finalType = resolveType(arrayType);
}
return t.Array(finalType);
}
default:
return t[type];
}
}

return t[type];
};

/**
* Map single argument to fcl.arg representation.
* @param {string} type - Cadence value type
Expand All @@ -101,15 +130,17 @@ export const mapArgument = (type, value) => {

case isArray(type): {
const arrayType = getArrayType(type);
const resolvedType = resolveType(type);

if (isComplexType(arrayType)) {
return fcl.arg(
value.map((v) => mapArgument(arrayType, v)),
t.Array(t[arrayType])
value,
// value.map((v) => mapArgument(arrayType, v)),
resolvedType
);
}

return fcl.arg(value, t.Array(t[arrayType]));
return fcl.arg(value, resolvedType);
}

case isDictionary(type): {
Expand Down
4 changes: 2 additions & 2 deletions src/interactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export const deployContract = async (props) => {
const template = update ? addContractTemplate : updateContractTemplate;

const hexedCode = hexContract(contractCode);
const args = [name, hexedCode]
const args = [name, hexedCode];
// Set roles
let ixProposer = to;
let ixPayer = to;
Expand All @@ -132,7 +132,7 @@ export const deployContract = async (props) => {
proposer: ixProposer,
signers: ixSigners,
code: template,
args
args,
});
};

Expand Down
2 changes: 1 addition & 1 deletion src/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const extract = (code, keyWord) => {
const match = target.match(/(?:\()(.*)(?:\))/);
if (match) {
if (match[1] === "") {
return []
return [];
}
return match[1].split(",").map((item) => item.replace(/\s*/g, ""));
}
Expand Down
1,051 changes: 876 additions & 175 deletions src/templates/index.js

Large diffs are not rendered by default.

167 changes: 129 additions & 38 deletions tests/args.test.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,104 @@
import { mapArgument, mapArguments, argType, mapValuesToCode } from "../src/args";
import { mapArgument, mapArguments, argType, mapValuesToCode, resolveType } from "../src/args";
import { toFixedValue, withPrefix } from "../src/fixer";
import { getTemplateInfo } from "../src/parser";

describe("argument types", ()=>{
test("Basic Type", async ()=>{
const input = "a: Int"
const expected = "Int"
const output = argType(input)
expect(output).toBe(expected)
})
test("Dictionary", async ()=>{
const input = "metadata : {String: String}"
const expected = "{String:String}"
const output = argType(input)
expect(output).toBe(expected)
})
test("Array", async ()=>{
const input = "list: [String]"
const expected = "[String]"
const output = argType(input)
expect(output).toBe(expected)
})
test("Array of Dictionaries", async ()=>{
const input = "list: [{String : String }]"
const expected = "[{String:String}]"
const output = argType(input)
expect(output).toBe(expected)
})
test("Dictionary of Arrays", async ()=>{
const input = "metadata: {UFix64:[String]}"
const expected = "{UFix64:[String]}"
const output = argType(input)
expect(output).toBe(expected)
})
})

describe("simple map", () => {
describe("argument types", () => {
test("Basic Type", async () => {
const input = "a: Int";
const expected = "Int";
const output = argType(input);
expect(output).toBe(expected);
});
test("Dictionary", async () => {
const input = "metadata : {String: String}";
const expected = "{String:String}";
const output = argType(input);
expect(output).toBe(expected);
});
test("Array", async () => {
const input = "list: [String]";
const expected = "[String]";
const output = argType(input);
expect(output).toBe(expected);
});
test("Array of Dictionaries", async () => {
const input = "list: [{String : String }]";
const expected = "[{String:String}]";
const output = argType(input);
expect(output).toBe(expected);
});
test("Dictionary of Arrays", async () => {
const input = "metadata: {UFix64:[String]}";
const expected = "{UFix64:[String]}";
const output = argType(input);
expect(output).toBe(expected);
});

test("Nested Arrays", async () => {
const input = "listOfLists: [[String]]";
const expected = "[[String]]";
const output = argType(input);
expect(output).toBe(expected);
});
});

describe("type resolvers", () => {
test("basic type - Address", () => {
const cases = [
{
type: "Address",
argInput: "0x11",
},
{
type: "String",
argInput: "Cadence",
},
{
type: "Int",
argInput: 42,
},
{
type: "UFix64",
argInput: "0.1337",
},
{
type: "Bool",
argInput: true,
},
];

for (let i = 0; i < cases.length; i++) {
const { type, argInput } = cases[i];
const output = resolveType(type);
const asArgument = output.asArgument(argInput);

expect(asArgument.type).toBe(type);
expect(asArgument.value.toString()).toBe(argInput.toString());
}
});
test("simple array = [String]", () => {
const input = "[String]";
const output = resolveType(input);
const argInput = ["Cadence"];
const asArgument = output.asArgument(argInput);

expect(output.label).toBe("Array");
expect(asArgument.value[0].type).toBe("String");
expect(asArgument.value[0].value).toBe(argInput[0]);
});
test("nested array - [[String]]", () => {
const input = "[[String]]";
const output = resolveType(input);
const argInput = [["Cadence"]];
const asArgument = output.asArgument(argInput);

expect(output.label).toBe("Array");
expect(asArgument.value[0].type).toBe("Array");
expect(asArgument.value[0].value.length).toBe(argInput[0].length);
});
});

describe("mapArgument", () => {
test("Int", async () => {
const type = "Int";
const input = 42;
Expand Down Expand Up @@ -153,11 +216,10 @@ describe("simple map", () => {
},
];
const output = mapArgument(type, input);

expect(output.xform.label).toBe("Array");
expect(output.value.length).toBe(input.length);
expect(output.value[0].value[0].key).toBe("balance");
expect(output.value[0].value[0].value).toBe("1.337");
expect(output.value[0].balance).toBe(input[0].balance);
});
test("Dictionaries of Arrays - {String: [String]}", async () => {
const type = "{String: String}";
Expand All @@ -174,6 +236,16 @@ describe("simple map", () => {
expect(output.value[0].value[1]).toBe("Bob");
expect(output.value[0].value[2]).toBe("Charlie");
});

test("Nested Array - [[String]]", async () => {
const type = "[[String]]";
const input = [["Cadence"]];
const output = mapArgument(type, input);

expect(output.xform.label).toBe("Array");
expect(output.value[0].length).toBe(input[0].length);
expect(output.value[0][0]).toBe(input[0][0]);
});
});

describe("complex example", () => {
Expand Down Expand Up @@ -227,3 +299,22 @@ describe("complex example", () => {
);
});
});

describe("mapValuesToCode", () => {
test("nested array", () => {
const code = `
pub fun main(list: [[Int]]){
log(list)
}
`;
const values = [[[1, 3, 3, 7]]];
const result = mapValuesToCode(code, values);
const [first] = result;

expect(first.xform.label).toBe("Array");
expect(first.value[0].length).toBe(values[0][0].length);
for (let i = 0; i < values[0][0].length; i++) {
expect(first.value[0][i]).toBe(values[0][0][i]);
}
});
});
1 change: 0 additions & 1 deletion tests/parser.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ describe("parser", () => {
expect(output.length).toBe(1);
});


test("extract transaction arguments - no arguments", () => {
const input = `
transaction {
Expand Down

0 comments on commit 57cfe79

Please sign in to comment.