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

custom asynchronous deserialization #85

Merged
merged 9 commits into from
Jul 23, 2018
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 1.2.1

* Introduced `customAsync`, to support asynchronous custom deserialization

# 1.1.12

* Improved documentation examples, see #43 by @brikou
Expand Down
76 changes: 49 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ When deserializing a model elememt / property, the following fields are availabl

## ModelSchema

[src/serializr.js:52-52](https://github.com/mobxjs/serializr/blob/8f0e459a1354a7f253a5c1c746269bfa71044093/src/serializr.js#L52-L52 "Source code on GitHub")
[src/serializr.js:52-52](https://github.com/evoye/serializr/blob/27e5a4ce9cd83f6fa0211f738517b61a2b1a4458/src/serializr.js#L52-L52 "Source code on GitHub")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The update of README.md is caused by the prepublish script declared in the package.json. All the links have been replaced with which related to you. You should discard the update of this. I will keep the readme being changed accidentally someday.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since I updated the docs for the custom propschema, the prepublish scripts needs to run to generate the respective readme chapter. Since I cannot do this for the official repo, maybe you can pull, run prepublish and commit the final readme before publishing to npm.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah ~ I and @alexggordon can do this after your PR merged.


JSDOC type defintions for usage w/o typescript.

Expand Down Expand Up @@ -341,7 +341,7 @@ Returns **any** void

## createSimpleSchema

[src/api/createSimpleSchema.js:17-24](https://github.com/mobxjs/serializr/blob/8f0e459a1354a7f253a5c1c746269bfa71044093/src/api/createSimpleSchema.js#L17-L24 "Source code on GitHub")
[src/api/createSimpleSchema.js:17-24](https://github.com/evoye/serializr/blob/27e5a4ce9cd83f6fa0211f738517b61a2b1a4458/src/api/createSimpleSchema.js#L17-L24 "Source code on GitHub")

Creates a model schema that (de)serializes from / to plain javascript objects.
Its factory method is: `() => ({})`
Expand All @@ -366,7 +366,7 @@ Returns **[object](#object)** model schema

## createModelSchema

[src/api/createModelSchema.js:29-47](https://github.com/mobxjs/serializr/blob/8f0e459a1354a7f253a5c1c746269bfa71044093/src/api/createModelSchema.js#L29-L47 "Source code on GitHub")
[src/api/createModelSchema.js:29-47](https://github.com/evoye/serializr/blob/27e5a4ce9cd83f6fa0211f738517b61a2b1a4458/src/api/createModelSchema.js#L29-L47 "Source code on GitHub")

Creates a model schema that (de)serializes an object created by a constructor function (class).
The created model schema is associated by the targeted type as default model schema, see setDefaultModelSchema.
Expand Down Expand Up @@ -399,7 +399,7 @@ Returns **[object](#object)** model schema

## getDefaultModelSchema

[src/api/getDefaultModelSchema.js:9-18](https://github.com/mobxjs/serializr/blob/8f0e459a1354a7f253a5c1c746269bfa71044093/src/api/getDefaultModelSchema.js#L9-L18 "Source code on GitHub")
[src/api/getDefaultModelSchema.js:9-18](https://github.com/evoye/serializr/blob/27e5a4ce9cd83f6fa0211f738517b61a2b1a4458/src/api/getDefaultModelSchema.js#L9-L18 "Source code on GitHub")

Returns the standard model schema associated with a class / constructor function

Expand All @@ -411,7 +411,7 @@ Returns **[ModelSchema](#modelschema)** model schema

## setDefaultModelSchema

[src/api/setDefaultModelSchema.js:15-18](https://github.com/mobxjs/serializr/blob/8f0e459a1354a7f253a5c1c746269bfa71044093/src/api/setDefaultModelSchema.js#L15-L18 "Source code on GitHub")
[src/api/setDefaultModelSchema.js:15-18](https://github.com/evoye/serializr/blob/27e5a4ce9cd83f6fa0211f738517b61a2b1a4458/src/api/setDefaultModelSchema.js#L15-L18 "Source code on GitHub")

Sets the default model schema for class / constructor function.
Everywhere where a model schema is required as argument, this class / constructor function
Expand All @@ -429,7 +429,7 @@ Returns **[ModelSchema](#modelschema)** model schema

## serializable

[src/api/serializable.js:93-103](https://github.com/mobxjs/serializr/blob/8f0e459a1354a7f253a5c1c746269bfa71044093/src/api/serializable.js#L93-L103 "Source code on GitHub")
[src/api/serializable.js:93-103](https://github.com/evoye/serializr/blob/27e5a4ce9cd83f6fa0211f738517b61a2b1a4458/src/api/serializable.js#L93-L103 "Source code on GitHub")

Decorator that defines a new property mapping on the default model schema for the class
it is used in.
Expand All @@ -453,7 +453,7 @@ Returns **PropertyDescriptor**

## serialize

[src/core/serialize.js:16-34](https://github.com/mobxjs/serializr/blob/8f0e459a1354a7f253a5c1c746269bfa71044093/src/core/serialize.js#L16-L34 "Source code on GitHub")
[src/core/serialize.js:16-34](https://github.com/evoye/serializr/blob/27e5a4ce9cd83f6fa0211f738517b61a2b1a4458/src/core/serialize.js#L16-L34 "Source code on GitHub")

Serializes an object (graph) into json using the provided model schema.
The model schema can be omitted if the object type has a default model schema associated with it.
Expand All @@ -468,7 +468,7 @@ Returns **[object](#object)** serialized representation of the object

## serializeAll

[src/core/serialize.js:89-100](https://github.com/mobxjs/serializr/blob/8f0e459a1354a7f253a5c1c746269bfa71044093/src/core/serialize.js#L89-L100 "Source code on GitHub")
[src/core/serialize.js:89-100](https://github.com/evoye/serializr/blob/27e5a4ce9cd83f6fa0211f738517b61a2b1a4458/src/core/serialize.js#L89-L100 "Source code on GitHub")

The `serializeAll` decorator can be used on a class to signal that all primitive properties should be serialized automatically.

Expand All @@ -478,7 +478,7 @@ The `serializeAll` decorator can be used on a class to signal that all primitive

## deserialize

[src/core/deserialize.js:38-56](https://github.com/mobxjs/serializr/blob/8f0e459a1354a7f253a5c1c746269bfa71044093/src/core/deserialize.js#L38-L56 "Source code on GitHub")
[src/core/deserialize.js:38-56](https://github.com/evoye/serializr/blob/27e5a4ce9cd83f6fa0211f738517b61a2b1a4458/src/core/deserialize.js#L38-L56 "Source code on GitHub")

Deserializes a json structor into an object graph.
This process might be asynchronous (for example if there are references with an asynchronous
Expand All @@ -497,7 +497,7 @@ Returns **([object](#object) \| [array](https://developer.mozilla.org/en-US/docs

## update

[src/core/update.js:21-42](https://github.com/mobxjs/serializr/blob/8f0e459a1354a7f253a5c1c746269bfa71044093/src/core/update.js#L21-L42 "Source code on GitHub")
[src/core/update.js:21-42](https://github.com/evoye/serializr/blob/27e5a4ce9cd83f6fa0211f738517b61a2b1a4458/src/core/update.js#L21-L42 "Source code on GitHub")

Similar to deserialize, but updates an existing object instance.
Properties will always updated entirely, but properties not present in the json will be kept as is.
Expand All @@ -513,7 +513,7 @@ Further this method behaves similar to deserialize.

## primitive

[src/types/primitive.js:17-29](https://github.com/mobxjs/serializr/blob/8f0e459a1354a7f253a5c1c746269bfa71044093/src/types/primitive.js#L17-L29 "Source code on GitHub")
[src/types/primitive.js:17-29](https://github.com/evoye/serializr/blob/27e5a4ce9cd83f6fa0211f738517b61a2b1a4458/src/types/primitive.js#L17-L29 "Source code on GitHub")

Indicates that this field contains a primitive value (or Date) which should be serialized literally to json.

Expand All @@ -532,7 +532,7 @@ Returns **[ModelSchema](#modelschema)**

## identifier

[src/types/identifier.js:42-56](https://github.com/mobxjs/serializr/blob/8f0e459a1354a7f253a5c1c746269bfa71044093/src/types/identifier.js#L42-L56 "Source code on GitHub")
[src/types/identifier.js:42-56](https://github.com/evoye/serializr/blob/27e5a4ce9cd83f6fa0211f738517b61a2b1a4458/src/types/identifier.js#L42-L56 "Source code on GitHub")

Similar to primitive, but this field will be marked as the identifier for the given Model type.
This is used by for example `reference()` to serialize the reference
Expand Down Expand Up @@ -572,13 +572,13 @@ Returns **PropSchema**

## date

[src/types/date.js:8-23](https://github.com/mobxjs/serializr/blob/8f0e459a1354a7f253a5c1c746269bfa71044093/src/types/date.js#L8-L23 "Source code on GitHub")
[src/types/date.js:8-23](https://github.com/evoye/serializr/blob/27e5a4ce9cd83f6fa0211f738517b61a2b1a4458/src/types/date.js#L8-L23 "Source code on GitHub")

Similar to primitive, serializes instances of Date objects

## alias

[src/types/alias.js:20-31](https://github.com/mobxjs/serializr/blob/8f0e459a1354a7f253a5c1c746269bfa71044093/src/types/alias.js#L20-L31 "Source code on GitHub")
[src/types/alias.js:20-31](https://github.com/evoye/serializr/blob/27e5a4ce9cd83f6fa0211f738517b61a2b1a4458/src/types/alias.js#L20-L31 "Source code on GitHub")

Alias indicates that this model property should be named differently in the generated json.
Alias should be the outermost propschema.
Expand All @@ -603,7 +603,7 @@ Returns **PropSchema**

## custom

[src/types/custom.js:36-45](https://github.com/mobxjs/serializr/blob/8f0e459a1354a7f253a5c1c746269bfa71044093/src/types/custom.js#L36-L45 "Source code on GitHub")
[src/types/custom.js:59-72](https://github.com/evoye/serializr/blob/27e5a4ce9cd83f6fa0211f738517b61a2b1a4458/src/types/custom.js#L59-L72 "Source code on GitHub")

Can be used to create simple custom propSchema. Multiple things can be done inside of a custom propSchema, like deserializing and serializing other (polymorphic) objects, skipping the serialization of something or checking the context of the obj being (de)serialized.

Expand All @@ -614,8 +614,11 @@ The `serializer` function has the signature:

When serializing the object `{a: 1}` the `serializer` function will be called with `serializer(1, 'a', {a: 1})`.

The `deserializer` function has the signature:
`(value, context) => void`
The `deserializer` function has the following signature for synchronous processing
`(value, context, oldValue) => void`

For asynchronous processing the function expects the following signature
`(value, context, oldValue, callback) => void`

When deserializing the object `{b: 2}` the `deserializer` function will be called with `deserializer(2, contextObj)` ([contextObj reference](https://github.com/mobxjs/serializr#deserialization-context)).

Expand All @@ -627,7 +630,7 @@ When deserializing the object `{b: 2}` the `deserializer` function will be calle
**Examples**

```javascript
var schema = _.createSimpleSchema({
var schemaDefault = _.createSimpleSchema({
a: _.custom(
function(v) {
return v + 2;
Expand All @@ -637,15 +640,34 @@ var schema = _.createSimpleSchema({
}
),
});
t.deepEqual(_.serialize(s, { a: 4 }), { a: 6 });
t.deepEqual(_.deserialize(s, { a: 6 }), { a: 4 });
t.deepEqual(_.serialize(schemaDefault, { a: 4 }), { a: 6 });
t.deepEqual(_.deserialize(schemaDefault, { a: 6 }), { a: 4 });

var schemaWithAsyncProps = _.createSimpleSchema({
a: _.customAsync(
function(v) {
return v + 2;
},
function(v, context, oldValue, callback) {
somePromise(v, context, oldValue).then((result) => {
callback(null, result - 2)
}.catch((err) => {
callback(err)
}
}
),
});
t.deepEqual(_.serialize(schemaWithAsyncProps, { a: 4 }), { a: 6 });
_.deserialize(schemaWithAsyncProps, { a: 6 }, (err, res) => {
t.deepEqual(res.a, 4)
};
```

Returns **PropSchema**

## object

[src/types/object.js:34-52](https://github.com/mobxjs/serializr/blob/8f0e459a1354a7f253a5c1c746269bfa71044093/src/types/object.js#L34-L52 "Source code on GitHub")
[src/types/object.js:34-52](https://github.com/evoye/serializr/blob/27e5a4ce9cd83f6fa0211f738517b61a2b1a4458/src/types/object.js#L34-L52 "Source code on GitHub")

`object` indicates that this property contains an object that needs to be (de)serialized
using its own model schema.
Expand Down Expand Up @@ -682,7 +704,7 @@ Returns **PropSchema**

## reference

[src/types/reference.js:65-98](https://github.com/mobxjs/serializr/blob/8f0e459a1354a7f253a5c1c746269bfa71044093/src/types/reference.js#L65-L98 "Source code on GitHub")
[src/types/reference.js:65-98](https://github.com/evoye/serializr/blob/27e5a4ce9cd83f6fa0211f738517b61a2b1a4458/src/types/reference.js#L65-L98 "Source code on GitHub")

`reference` can be used to (de)serialize references that point to other models.

Expand Down Expand Up @@ -746,7 +768,7 @@ Returns **PropSchema**

## list

[src/types/list.js:33-54](https://github.com/mobxjs/serializr/blob/8f0e459a1354a7f253a5c1c746269bfa71044093/src/types/list.js#L33-L54 "Source code on GitHub")
[src/types/list.js:33-54](https://github.com/evoye/serializr/blob/27e5a4ce9cd83f6fa0211f738517b61a2b1a4458/src/types/list.js#L33-L54 "Source code on GitHub")

List indicates that this property contains a list of things.
Accepts a sub model schema to serialize the contents
Expand Down Expand Up @@ -784,7 +806,7 @@ Returns **PropSchema**

## map

[src/types/map.js:13-62](https://github.com/mobxjs/serializr/blob/8f0e459a1354a7f253a5c1c746269bfa71044093/src/types/map.js#L13-L62 "Source code on GitHub")
[src/types/map.js:13-62](https://github.com/evoye/serializr/blob/27e5a4ce9cd83f6fa0211f738517b61a2b1a4458/src/types/map.js#L13-L62 "Source code on GitHub")

Similar to list, but map represents a string keyed dynamic collection.
This can be both plain objects (default) or ES6 Map like structures.
Expand All @@ -796,7 +818,7 @@ This will be inferred from the initial value of the targetted attribute.

## mapAsArray

[src/types/mapAsArray.js:15-52](https://github.com/mobxjs/serializr/blob/8f0e459a1354a7f253a5c1c746269bfa71044093/src/types/mapAsArray.js#L15-L52 "Source code on GitHub")
[src/types/mapAsArray.js:15-52](https://github.com/evoye/serializr/blob/27e5a4ce9cd83f6fa0211f738517b61a2b1a4458/src/types/mapAsArray.js#L15-L52 "Source code on GitHub")

Similar to map, mapAsArray can be used to serialize a map-like collection where the key is contained in the 'value object'.
Example: consider Map<id: number, customer: Customer> where the Customer object has the id stored on itself.
Expand All @@ -811,7 +833,7 @@ For ES6 maps this has the benefit of being allowed to have non-string keys in th

## raw

[src/types/raw.js:15-24](https://github.com/mobxjs/serializr/blob/8f0e459a1354a7f253a5c1c746269bfa71044093/src/types/raw.js#L15-L24 "Source code on GitHub")
[src/types/raw.js:15-24](https://github.com/evoye/serializr/blob/27e5a4ce9cd83f6fa0211f738517b61a2b1a4458/src/types/raw.js#L15-L24 "Source code on GitHub")

Indicates that this field is only need to putted in the serialized json or
deserialized instance, without any transformations. Stay with its original value
Expand All @@ -831,7 +853,7 @@ Returns **[ModelSchema](#modelschema)**

## SKIP

[src/constants.js:20-20](https://github.com/mobxjs/serializr/blob/8f0e459a1354a7f253a5c1c746269bfa71044093/src/constants.js#L20-L20 "Source code on GitHub")
[src/constants.js:20-20](https://github.com/evoye/serializr/blob/27e5a4ce9cd83f6fa0211f738517b61a2b1a4458/src/constants.js#L20-L20 "Source code on GitHub")

In the event that a property needs to be deserialized, but not serialized, you can use the SKIP symbol to omit the property. This has to be used with the custom serializer.

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,4 @@
"typescript": "^2.1.4",
"uglify-js": "^2.6.4"
}
}
}
5 changes: 3 additions & 2 deletions serializr.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,9 @@ export function map(propSchema: PropSchema): PropSchema;

export function mapAsArray(propSchema: PropSchema, keyPropertyName: string): PropSchema;

export function custom(serializer: (value: any) => any, deserializer: (jsonValue: any) => any): PropSchema;
export function custom(serializer: (value: any) => any, deserializer: (jsonValue: any, context?: any, oldValue?: any) => any): PropSchema;
export function custom(serializer: (value: any) => any, deserializer: (jsonValue: any, context: any, oldValue: any, callback: (err: any, result: any) => void) => any): PropSchema;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An empty line break for readability?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currently, line breaks are only between different functions, not signature alternatives. We just introduced the latter with the final version.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Your explanation is reasonable.


export function serializeAll<T extends Function>(clazz: T): T

export const SKIP: {}
export const SKIP: {}
43 changes: 35 additions & 8 deletions src/types/custom.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { invariant } from "../utils/utils"
import {invariant} from "../utils/utils"

/**
* Can be used to create simple custom propSchema. Multiple things can be done inside of a custom propSchema, like deserializing and serializing other (polymorphic) objects, skipping the serialization of something or checking the context of the obj being (de)serialized.
Expand All @@ -10,13 +10,16 @@ import { invariant } from "../utils/utils"

* When serializing the object `{a: 1}` the `serializer` function will be called with `serializer(1, 'a', {a: 1})`.

* The `deserializer` function has the signature:
* `(value, context) => void`
* The `deserializer` function has the following signature for synchronous processing
* `(value, context, oldValue) => void`

* For asynchronous processing the function expects the following signature
* `(value, context, oldValue, callback) => void`

* When deserializing the object `{b: 2}` the `deserializer` function will be called with `deserializer(2, contextObj)` ([contextObj reference](https://github.com/mobxjs/serializr#deserialization-context)).
*
* @example
* var schema = _.createSimpleSchema({
* var schemaDefault = _.createSimpleSchema({
* a: _.custom(
* function(v) {
* return v + 2;
Expand All @@ -26,20 +29,44 @@ import { invariant } from "../utils/utils"
* }
* ),
* });
* t.deepEqual(_.serialize(s, { a: 4 }), { a: 6 });
* t.deepEqual(_.deserialize(s, { a: 6 }), { a: 4 });
* t.deepEqual(_.serialize(schemaDefault, { a: 4 }), { a: 6 });
* t.deepEqual(_.deserialize(schemaDefault, { a: 6 }), { a: 4 });
*
* var schemaWithAsyncProps = _.createSimpleSchema({
* a: _.customAsync(
* function(v) {
* return v + 2;
* },
* function(v, context, oldValue, callback) {
* somePromise(v, context, oldValue).then((result) => {
* callback(null, result - 2)
* }.catch((err) => {
* callback(err)
* }
* }
* ),
* });
* t.deepEqual(_.serialize(schemaWithAsyncProps, { a: 4 }), { a: 6 });
* _.deserialize(schemaWithAsyncProps, { a: 6 }, (err, res) => {
* t.deepEqual(res.a, 4)
* };

*
* @param {function} serializer function that takes a model value and turns it into a json value
* @param {function} deserializer function that takes a json value and turns it into a model value. It also takes context argument, which can allow you to deserialize based on the context of other parameters.
* @returns {PropSchema}
*/
export default function custom(serializer, deserializer) {
invariant(typeof serializer === "function", "first argument should be function")
invariant(typeof deserializer === "function", "second argument should be function")
invariant((typeof deserializer === "function"), "second argument should be a function or promise")
return {
serializer: serializer,
deserializer: function (jsonValue, done, context, oldValue) {
done(null, deserializer(jsonValue, context, oldValue))
if (deserializer.length === 4) {
deserializer(jsonValue, context, oldValue, done)
} else {
done(null, deserializer(jsonValue, context, oldValue))
}
}
}
}
Loading