morphism
morphism copied to clipboard
Async morphism
Is your feature request related to a problem? Please describe. Morphism is unable to resolve promises in ActionFunction and ActionSelector and Type Promise is not allowed.
Describe the solution you'd like Allow ActionFunction and ActionSelector to return a Promise that would later be resolvable by say a morphismasync method.
Example Schema
// Schema type removed to allow for group to be of type promise
const customerSchema = {
firstname: "firstname",
lastname: "lastname",
email: "email",
group: {
path: "group",
fn: async value => {
const res = await callSomeService(value);
return res.data;
}
}
};
Describe alternatives you've considered Alternative solutions would be to map the data available with one morphism call then make any async requests desired then remap the data returned from those async requests.
Another solution would be to add a helper function like so.
async function resolvePromises(objectFromMorph) {
for (const key of Object.keys(objectFromMorph)) {
const value = objectFromMorph[key];
if (Promise.resolve(value) == value) {
console.log("Found Promise", value);
objectFromMorph[key] = await value;
}
}
return objectFromMorph;
}
const morphed = morphism(customerSchema, customer.data);
const customer = resolvePromises(morphed);
{
"sourceCustomerId": 39392,
"firstname": "somefirstname",
"lastname": "somelastname",
"email": "[email protected]",
"group": "Promise { <pending> }"
}
// After resolvePromises
{
"sourceCustomerId": 39392,
"firstname": "somefirstname",
"lastname": "somelastname",
"email": "[email protected]",
"group": "somegroup"
}
Additional context Ideal implementation
const morphed = morphism(customerSchema, customer.data)
Current result, with unresolved promise
{
"firstname": "somefirstname",
"lastname": "somelastname",
"email": "[email protected]",
"group": "Promise { <pending> }"
}
Ideal result, with resolved promise
{
"firstname": "somefirstname",
"lastname": "somelastname",
"email": "[email protected]",
"group": "somegroup"
}
@SamuelColacchia Thank you for using Morphism
and bringing this feature on the table. I was thinking about having an async
interface on Morphism
for several purposes (parallelization, async transformations...). It might be a good start.
My first thought was to have an .async
property available on morphism
like:
const customerSchema = {
firstname: "firstname",
lastname: "lastname",
email: "email",
group: {
path: "group",
fn: async value => {
const res = await callSomeService(value);
return res.data;
}
}
};
const result = await morphism.async(customerSchema, input)
// ==>
// {
// "firstname": "somefirstname",
// "lastname": "somelastname",
// "email": "[email protected]",
// "group": "somegroup"
// }
Mostly to keep the backward compatibility on the synchronous interface and provide the appropriate typing with TypeScript.
What do you think about this implementation ?
@emyann That seems like it would be a good solution to the problem. Thinking it over in my head it would require the caller of morphism to explicitly want to wait for a async request, which I think is a good approach.
@SamuelColacchia Awesome! Thank you for your feedback, I'm going to schedule this feature on the next
branch as a beta feature. I'll get back to you for testing purposes if you're ok with it :)
@emyann Sounds good to me.
@emyann Hi, thanks for the library, it's nice and compact! How does the feature live? :)
@kirsar Thank you for the feedback! I'm actively working on a big chunk of the library which is Data Validation
that should land soon on the beta
branch (https://github.com/nobrainr/morphism/releases)
Working on that make me realize that I needed to support async validations also, so I'm ideating this async part of morphism
but I do want to have a clear separation between side effects and the actions of transforming the data itself, that in my opinion should stay as pure as possible.
I'll likely come up by the end of this month with a design on how I envision this in Morphism
, and in the meantime I would love to hear about your use-case with that feature if you're interested 🙂
Thanks for reply. My case is very simple: I need to talk with 3rd party via some amqp and use morphism to map 'my' entities to 'their' commands / events. And I want to separate mapping config (schema) away from other complexity, but sometimes I need to make async calls to db to reconstruct entity from event (3rd party is not under my control), like:
{
id: 'id',
stateId: event => (await stateRepo.getByCode(event.state)).id,
}