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

adds decoder support to fragment definition #80

Merged
Merged
Show file tree
Hide file tree
Changes from all 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
60 changes: 53 additions & 7 deletions src/base/result_decoder.re
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,45 @@ let rec unify_document_schema = (config, document) => {
...rest,
] => [
{
open Result;

let with_decoder =
switch (fg_directives |> find_directive("bsDecoder")) {
| None => Ok(None)
| Some({item: {d_arguments, _}, span}) =>
switch (find_argument("fn", d_arguments)) {
| None =>
Error(
make_error(
error_marker,
config.map_loc,
span,
"bsDecoder must be given 'fn' argument",
),
)
| Some((_, {item: Iv_string(fn_name), span})) =>
Ok(
Some(
structure =>
Res_custom_decoder(
config.map_loc(span),
fn_name,
structure,
),
),
)
| Some((_, {span, _})) =>
Error(
make_error(
error_marker,
config.map_loc,
span,
"The 'fn' argument must be a string",
),
)
}
};
Comment on lines +580 to +615
Copy link
Contributor Author

@mbirkegaard mbirkegaard Feb 17, 2020

Choose a reason for hiding this comment

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

There should in principle be no problems abstracting this out and using it for fields, fragments and operations.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm ok with leaving it as is. A lot will change with #67 and we'll have to do major code cleanup after that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got it.


let is_record = has_directive("bsRecord", fg_directives);
switch (Schema.lookup_type(config.schema, fg_type_condition.item)) {
| None =>
Expand All @@ -601,13 +640,20 @@ let rec unify_document_schema = (config, document) => {
Some(fg_selection_set),
);

Mod_fragment(
fg_name.item,
[],
error_marker.has_error,
fg,
structure,
);
switch (with_decoder) {
| Error(err) => Mod_fragment(fg_name.item, [], true, fg, err)
| Ok(decoder) =>
Mod_fragment(
fg_name.item,
[],
error_marker.has_error,
fg,
switch (decoder) {
| Some(decoder) => decoder(structure)
| None => structure
},
)
};
Comment on lines +643 to +656
Copy link
Contributor Author

@mbirkegaard mbirkegaard Feb 17, 2020

Choose a reason for hiding this comment

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

There are a couple of different ways of doing this, but to me this seemed the most legible to newcomers to the library.

};
},
...unify_document_schema(config, rest),
Expand Down
53 changes: 39 additions & 14 deletions tests_bucklescript/__tests__/fragmentDefinition.re
Original file line number Diff line number Diff line change
@@ -1,9 +1,29 @@
type record = {
nullableOfNullable: option(array(option(string))),
nullableOfNonNullable: option(array(string)),
};

let concat = ({nullableOfNullable, nullableOfNonNullable}) => {
let x =
nullableOfNullable
->Belt.Option.getWithDefault([||])
->Belt.Array.keepMap(x => x);
let y = nullableOfNonNullable->Belt.Option.getWithDefault([||]);

Belt.Array.concat(x, y);
};

module Fragments = [%graphql
{|
fragment listFragment on Lists {
nullableOfNullable
nullableOfNonNullable
}

fragment concatFragment on Lists @bsRecord @bsDecoder(fn: "concat") {
nullableOfNullable
nullableOfNonNullable
}
|}
];

Expand All @@ -18,6 +38,10 @@ module MyQuery = [%graphql
...Fragments.ListFragment @bsField(name: "frag1")
...Fragments.ListFragment @bsField(name: "frag2")
}

l3: lists {
...Fragments.ConcatFragment
}
}
|}
];
Expand All @@ -26,34 +50,35 @@ open Jest;
open Expect;

describe("Fragment definition", () => {
let expectedObject = {
"nullableOfNullable": Some([|Some("a"), None, Some("b")|]),
"nullableOfNonNullable": None,
};

test("Decodes the fragment", () =>
{|
{
"l1": {"nullableOfNullable": ["a", null, "b"]},
"l2": {"nullableOfNullable": ["a", null, "b"]}
"l2": {"nullableOfNullable": ["a", null, "b"]},
"l3": {
"nullableOfNullable": ["a", null, "b", null, "c"],
"nullableOfNonNullable": ["d", "e"]
}
}|}
|> Js.Json.parseExn
|> MyQuery.parse
|> expect
|> toEqual({
"l1": {
"nullableOfNullable": Some([|Some("a"), None, Some("b")|]),
"nullableOfNonNullable": None,
},
"l1": expectedObject,
"l2": {
"frag1": {
"nullableOfNullable": Some([|Some("a"), None, Some("b")|]),
"nullableOfNonNullable": None,
},
"frag2": {
"nullableOfNullable": Some([|Some("a"), None, Some("b")|]),
"nullableOfNonNullable": None,
},
"frag1": expectedObject,
"frag2": expectedObject,
},
"l3": [|"a", "b", "c", "d", "e"|],
})
);

test("Removes @bsField from query output", () =>
MyQuery.query |> Js.String.includes("@bsField") |> expect |> toBe(false)
);
});
});
8 changes: 7 additions & 1 deletion tests_bucklescript/__tests__/fragmentDefinition.rei
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
type record = {
nullableOfNullable: option(array(option(string))),
nullableOfNonNullable: option(array(string)),
};

module Fragments: {
module ListFragment: {
type t = {
Expand Down Expand Up @@ -33,6 +38,7 @@ module MyQuery: {
"nullableOfNonNullable": option(array(string)),
},
},
"l3": array(string),
};

let make:
Expand All @@ -52,4 +58,4 @@ module MyQuery: {
"variables": Js.Json.t,
};
let makeVariables: unit => Js.Json.t;
};
};
44 changes: 41 additions & 3 deletions tests_native/fragment_definition.re
Original file line number Diff line number Diff line change
@@ -1,11 +1,38 @@
open Test_shared;

type record = {
nullableOfNullable: option(array(option(string))),
nullableOfNonNullable: option(array(string)),
};

let concat = ({nullableOfNullable, nullableOfNonNullable}) => {
let x = switch (nullableOfNullable) {
| None => [||]
| Some(arr) => arr |> Array.map(v => switch(v) {
| None => [||]
| Some(s) => [|s|]
}) |> Array.to_list |> Array.concat
};

let y = switch (nullableOfNonNullable) {
| None => [||]
| Some(a) => a
};

Array.append(x, y);
};

module Fragments = [%graphql
{|
fragment listFragment on Lists {
nullableOfNullable
nullableOfNonNullable
}

fragment concatFragment on Lists @bsRecord @bsDecoder(fn: "concat") {
nullableOfNullable
nullableOfNonNullable
}
|}
];

Expand All @@ -26,6 +53,10 @@ module MyQuery = [%graphql
...Fragments.ListFragment @bsField(name: "frag1")
...Fragments.ListFragment @bsField(name: "frag2")
}

l3: lists {
...Fragments.ConcatFragment
}
}
|}
];
Expand All @@ -38,6 +69,7 @@ type qt = {
frag1: ft,
frag2: ft,
},
l3: array(string)
};

let print_fragment = (formatter, obj: ft) =>
Expand Down Expand Up @@ -84,7 +116,8 @@ let my_query: module Alcotest.TESTABLE with type t = qt =
let equal = (a, b) =>
fragment_equal(a#l1, b#l1)
&& fragment_equal(a#l2#frag1, b#l2#frag1)
&& fragment_equal(a#l2#frag2, b#l2#frag2);
&& fragment_equal(a#l2#frag2, b#l2#frag2)
&& a#l3 == b#l3;
});

let decodes_the_fragment = () =>
Expand All @@ -96,7 +129,11 @@ let decodes_the_fragment = () =>
{|
{
"l1": {"nullableOfNullable": ["a", null, "b"]},
"l2": {"nullableOfNullable": ["a", null, "b"]}
"l2": {"nullableOfNullable": ["a", null, "b"]},
"l3": {
"nullableOfNullable": ["a", null, "b", null, "c"],
"nullableOfNonNullable": ["d", "e"]
}
}|},
),
),
Expand All @@ -119,7 +156,8 @@ let decodes_the_fragment = () =>
pub nullableOfNullable = Some([|Some("a"), None, Some("b")|]);
pub nullableOfNonNullable = None
}
}
};
pub l3 = [|"a", "b", "c", "d", "e"|]
},
);

Expand Down