Skip to content

Commit

Permalink
refactor(aci): rework Sophia Option type representation (#691)
Browse files Browse the repository at this point in the history
Change Option type Representation from Promise to Value(Some) and undefined(None)

BREAKING CHANGE: Change Sophia option type representation in ACI

689
  • Loading branch information
nduchak committed Sep 30, 2019
1 parent 0d43804 commit 0dbb2fe
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 37 deletions.
39 changes: 18 additions & 21 deletions es/contract/aci/transformation.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,21 +94,20 @@ export function readType (type, { bindings } = {}) {
* @param bindings
* @return {string}
*/
export async function transform (type, value, { bindings } = {}) {
export function transform (type, value, { bindings } = {}) {
const { t, generic } = readType(type, { bindings })

switch (t) {
case SOPHIA_TYPES.string:
return `"${value}"`
case SOPHIA_TYPES.list:
return `[${await Promise.all(value.map(async el => transform(generic, el, { bindings })))}]`
return `[${value.map(el => transform(generic, el, { bindings }))}]`
case SOPHIA_TYPES.tuple:
return `(${await Promise.all(value.map(async (el, i) => transform(generic[i], el, {
return `(${value.map((el, i) => transform(generic[i], el, {
bindings
})))})`
}))})`
case SOPHIA_TYPES.option: {
const optionV = await value.catch(e => undefined)
return optionV === undefined ? 'None' : `Some(${await transform(generic, optionV, {
return value === undefined ? 'None' : `Some(${transform(generic, value, {
bindings
})})`
}
Expand All @@ -117,10 +116,9 @@ export async function transform (type, value, { bindings } = {}) {
case SOPHIA_TYPES.signature:
return `#${typeof value === 'string' ? value : Buffer.from(value).toString('hex')}`
case SOPHIA_TYPES.record:
return `{${await generic.reduce(
async (acc, { name, type }, i) => {
acc = await acc
acc += `${i !== 0 ? ',' : ''}${name} = ${await transform(type, value[name], {
return `{${generic.reduce(
(acc, { name, type }, i) => {
acc += `${i !== 0 ? ',' : ''}${name} = ${transform(type, value[name], {
bindings
})}`
return acc
Expand All @@ -136,33 +134,32 @@ export async function transform (type, value, { bindings } = {}) {
return `${value}`
}

async function transformVariant (value, generic, { bindings }) {
function transformVariant (value, generic, { bindings }) {
const [[variant, variantArgs]] = typeof value === 'string' ? [[value, []]] : Object.entries(value)
const [[v, type]] = Object.entries(generic.find(o => Object.keys(o)[0].toLowerCase() === variant.toLowerCase()))
return `${v}${!type.length
? ''
: `(${await Promise.all(variantArgs.slice(0, type.length).map(async (el, i) => transform(type[i], el, {
: `(${variantArgs.slice(0, type.length).map((el, i) => transform(type[i], el, {
bindings
})))})`
}))})`
}`
}

export async function transformMap (value, generic, { bindings }) {
export function transformMap (value, generic, { bindings }) {
if (value instanceof Map) {
value = Array.from(value.entries())
}
if (!Array.isArray(value) && value instanceof Object) {
value = Object.entries(value)
}

return `{${await value
return `{${value
.reduce(
async (acc, [key, value], i) => {
acc = await acc
(acc, [key, value], i) => {
if (i !== 0) acc += ','
acc += `[${await transform(generic[0], key, {
acc += `[${transform(generic[0], key, {
bindings
})}] = ${await transform(generic[1], value, { bindings })}`
})}] = ${transform(generic[1], value, { bindings })}`
return acc
},
''
Expand Down Expand Up @@ -285,7 +282,7 @@ export function prepareSchema (type, { bindings } = {}) {
case SOPHIA_TYPES.signature:
return JoiBinary.binary().bufferCheck(64).error(getJoiErrorMsg)
case SOPHIA_TYPES.option:
return Joi.object().type(Promise).error(getJoiErrorMsg)
return prepareSchema(generic, { bindings }).optional().error(getJoiErrorMsg)
// @Todo Need to transform Map to Array of arrays before validating it
// case SOPHIA_TYPES.map:
// return Joi.array().items(Joi.array().ordered(generic.map(type => prepareSchema(type))))
Expand Down Expand Up @@ -373,7 +370,7 @@ export function validateArguments (aci, params) {
const validationSchema = Joi.array().ordered(
aci.arguments
.map(({ type }, i) => prepareSchema(type, { bindings: aci.bindings }).label(`[${params[i]}]`))
).label('Argument')
).sparse(true).label('Argument')
const { error } = Joi.validate(params, validationSchema, { abortEarly: false })
if (error) {
throw error
Expand Down
32 changes: 16 additions & 16 deletions test/integration/contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,22 +226,22 @@ describe('Contract', function () {
isCompiled.should.be.equal(true)
})
it('Dry-run deploy fn', async () => {
const res = await contractObject.methods.init.get('123', 1, Promise.resolve('hahahaha'))
const res = await contractObject.methods.init.get('123', 1, 'hahahaha')
res.result.should.have.property('gasUsed')
res.result.should.have.property('returnType')
})
it('Dry-run deploy fn on specific account', async () => {
const current = await contract.address()
const onAccount = contract.addresses().find(acc => acc !== current)
const { result } = await contractObject.methods.init.get('123', 1, Promise.resolve('hahahaha'), { onAccount })
const { result } = await contractObject.methods.init.get('123', 1, 'hahahaha', { onAccount })
result.should.have.property('gasUsed')
result.should.have.property('returnType')
result.callerId.should.be.equal(onAccount)
})

it('Deploy contract before compile', async () => {
contractObject.compiled = null
await contractObject.methods.init('123', 1, Promise.resolve('hahahaha'))
await contractObject.methods.init('123', 1, 'hahahaha')
const isCompiled = contractObject.compiled.length && contractObject.compiled.slice(0, 3) === 'cb_'
isCompiled.should.be.equal(true)
})
Expand Down Expand Up @@ -383,22 +383,22 @@ describe('Contract', function () {
})
it('Map With Option Value', async () => {
const address = await contract.address()
let mapArgWithSomeValue = new Map(
const mapArgWithSomeValue = new Map(
[
[address, ['someStringV', Promise.resolve(123)]]
[address, ['someStringV', 123]]
]
)
let mapArgWithNoneValue = new Map(
const mapArgWithNoneValue = new Map(
[
[address, ['someStringV', Promise.reject(Error()).catch(e => undefined)]]
[address, ['someStringV', undefined]]
]
)
let returnArgWithSomeValue = new Map(
const returnArgWithSomeValue = new Map(
[
[address, ['someStringV', 123]]
]
)
let returnArgWithNoneValue = new Map(
const returnArgWithNoneValue = new Map(
[
[address, ['someStringV', undefined]]
]
Expand Down Expand Up @@ -435,7 +435,7 @@ describe('Contract', function () {
describe('RECORD/STATE', function () {
const objEq = (obj, obj2) => !Object.entries(obj).find(([key, val]) => JSON.stringify(obj2[key]) !== JSON.stringify(val))
it('Valid Set Record (Cast from JS object)', async () => {
await contractObject.methods.setRecord({ value: 'qwe', key: 1234, testOption: Promise.resolve('test') })
await contractObject.methods.setRecord({ value: 'qwe', key: 1234, testOption: 'test' })
const state = await contractObject.methods.getRecord()

objEq(state.decodedResult, { value: 'qwe', key: 1234, testOption: 'test' }).should.be.equal(true)
Expand All @@ -445,7 +445,7 @@ describe('Contract', function () {
objEq(result.decodedResult, { value: 'qwe', key: 1234, testOption: 'test' }).should.be.equal(true)
})
it('Get Record With Option (Convert to JS object)', async () => {
await contractObject.methods.setRecord({ key: 1234, value: 'qwe', testOption: Promise.resolve('resolved string') })
await contractObject.methods.setRecord({ key: 1234, value: 'qwe', testOption: 'resolved string' })
const result = await contractObject.methods.getRecord()
objEq(result.decodedResult, { value: 'qwe', key: 1234, testOption: 'resolved string' }).should.be.equal(true)
})
Expand All @@ -459,25 +459,25 @@ describe('Contract', function () {
})
describe('OPTION', function () {
it('Set Some Option Value(Cast from JS value/Convert result to JS)', async () => {
const optionRes = await contractObject.methods.intOption(Promise.resolve(123))
const optionRes = await contractObject.methods.intOption(123)

optionRes.decodedResult.should.be.equal(123)
})
it('Set Some Option List Value(Cast from JS value/Convert result to JS)', async () => {
const optionRes = await contractObject.methods.listOption(Promise.resolve([[1, 'testString']]))
const optionRes = await contractObject.methods.listOption([[1, 'testString']])

JSON.stringify(optionRes.decodedResult).should.be.equal(JSON.stringify([[1, 'testString']]))
})
it('Set None Option Value(Cast from JS value/Convert to JS)', async () => {
const optionRes = await contractObject.methods.intOption(Promise.reject(Error()))
const optionRes = await contractObject.methods.intOption(undefined)
const isUndefined = optionRes.decodedResult === undefined
isUndefined.should.be.equal(true)
})
it('Invalid option type', async () => {
try {
await contractObject.methods.intOption({ s: 2 })
await contractObject.methods.intOption('string')
} catch (e) {
e.message.should.be.equal('"Argument" at position 0 fails because [Value \'[[object Object]]\' at path: [0] not a Promise]')
e.message.should.be.equal('"Argument" at position 0 fails because [Value "[string]" at path: [0] not a number]')
}
})
})
Expand Down

0 comments on commit 0dbb2fe

Please sign in to comment.