Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async morphism #106

Open
SamuelColacchia opened this issue Jul 25, 2019 · 7 comments
Open

Async morphism #106

SamuelColacchia opened this issue Jul 25, 2019 · 7 comments

Comments

@SamuelColacchia
Copy link

SamuelColacchia commented Jul 25, 2019

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"
}
@emyann
Copy link
Member

emyann commented Jul 26, 2019

@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 ?

@SamuelColacchia
Copy link
Author

@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.

@emyann
Copy link
Member

emyann commented Jul 30, 2019

@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 :)

@SamuelColacchia
Copy link
Author

@emyann Sounds good to me.

@kirsar
Copy link

kirsar commented May 7, 2020

@emyann Hi, thanks for the library, it's nice and compact! How does the feature live? :)

@emyann
Copy link
Member

emyann commented May 7, 2020

@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 🙂

@kirsar
Copy link

kirsar commented May 7, 2020

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,
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants