Skip to content
This repository was archived by the owner on Apr 14, 2022. It is now read-only.

Add creation of avro-schema from query with multi-head connections, Unions, Maps and directives #201

Merged
merged 7 commits into from
Aug 10, 2018

Conversation

SudoBobo
Copy link
Contributor

@SudoBobo SudoBobo commented Aug 2, 2018

Also changed GraphQL Map type: now it has subType = Map and name = values.type .. '_Map'
(Example: 'String_Map') This is to avoid name clash.
Сloses #200 #198 #197 #85 #84

@SudoBobo SudoBobo requested a review from Totktonada August 2, 2018 19:37
@SudoBobo SudoBobo force-pushed the union_map_directive_to_avro branch 4 times, most recently from a1808e0 to e951857 Compare August 3, 2018 16:40
@SudoBobo SudoBobo force-pushed the union_map_directive_to_avro branch from e951857 to ed13524 Compare August 6, 2018 10:58
Copy link
Member

@Totktonada Totktonada left a comment

Choose a reason for hiding this comment

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

Comments for maps generation.

@@ -29,7 +33,9 @@ local gql_scalar_to_avro_index = {

local function gql_scalar_to_avro(fieldType)
assert(fieldType.__type == "Scalar", "GraphQL scalar field expected")
assert(fieldType.name ~= "Map", "Map type is not supported")
if (fieldType.subtype == "Map") then
Copy link
Member

Choose a reason for hiding this comment

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

Redundant parenthesis.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, fixed.


local res = core_types.map
local res = core_types.map({values = converted_values, name = map_name })
Copy link
Member

Choose a reason for hiding this comment

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

Code style: no whitespace after '{' and whitespace before '}' (I prefer to avoid these whitespaces.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed to 'no whitespaces'

local converted_values = types.convert(state, avro_schema.values,
{context = context})

local true_values_type = converted_values
Copy link
Member

Choose a reason for hiding this comment

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

Use core_types.nullable().

Copy link
Member

Choose a reason for hiding this comment

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

BTW, why do you need this?

Copy link
Member

Choose a reason for hiding this comment

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

BTW, suggest to use 'bare' or 'nullable' name depending of what do you need. The functions named in the same way are in 'core.types'.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed with core_types.nullable() and 'nullable_values_type'.
We need this to name Map type properly. Previously we have single Map type which has no info about values type. Now we have ValuesType_Map GraphQL type for each avro-schema map type. Like this:

{"type": "map", "values": "string"} -> String_Map

Copy link
Contributor Author

@SudoBobo SudoBobo Aug 7, 2018

Choose a reason for hiding this comment

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

After verbal discussion we decided not to name Map types like ValuesType_Map and use context.path .. Map names instead.

@@ -38,6 +38,9 @@ local function box_type(type_to_box, box_field_name, opts)
-- for the argument type to avoid 'Encountered multiple types' error. See
-- also the comment in @{types.box_collection_type}.
local box_name = (gql_true_type.name or gql_true_type.__type) .. '_box'
if (gql_true_type.__type == "Scalar" and gql_true_type.subtype == 'Map') then
Copy link
Member

Choose a reason for hiding this comment

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

You are use two types of quotation marks in the one line.

Copy link
Member

Choose a reason for hiding this comment

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

BTW, redundant parenthesis.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, fixed.

end,
})
function types.map(config)
local instance = {
Copy link
Member

Choose a reason for hiding this comment

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

2 spaces indent in this file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

'we should not go here')
end,
})
function types.map(config)
Copy link
Member

Choose a reason for hiding this comment

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

Add nonNull into the instance (see types.inputMap).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought we use another mechanism for setting nullability.
Like this:

local res = core_types.map({values = converted_values, name = map_name})
return avro_t == 'map' and core_types.nonNull(res) or res

We do like this with records, arrays, etc. Why we may want to handle maps different way?

Copy link
Member

Choose a reason for hiding this comment

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

We don’t, but I propose to support it in the type object for unification with other type objects.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, okay, 'for unification' is a valid argument for me.

--- The function converts a GraphQL Map type to avro-schema map type.
map_to_avro = function(mapType)
assert(mapType.values ~= nil, "GraphQL Map type must have 'values' field")
return {
Copy link
Member

Choose a reason for hiding this comment

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

Double whitespace.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

@@ -38,6 +38,9 @@ local function box_type(type_to_box, box_field_name, opts)
-- for the argument type to avoid 'Encountered multiple types' error. See
-- also the comment in @{types.box_collection_type}.
local box_name = (gql_true_type.name or gql_true_type.__type) .. '_box'
if (gql_true_type.__type == "Scalar" and gql_true_type.subtype == 'Map') then
box_name = 'Map_box'
Copy link
Member

Choose a reason for hiding this comment

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

Is it for compatibility? For cases when a map is inside a union?

Copy link
Contributor Author

@SudoBobo SudoBobo Aug 7, 2018

Choose a reason for hiding this comment

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

Exactly. Previously we created box_name = ScalarType.name .. '_box' and this covered Map type case as well.
But now Map type has some custom name, not 'Map', so we have to manually name box_type.

Copy link
Member

Choose a reason for hiding this comment

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

So, please, let short comment here.

Copy link
Member

Choose a reason for hiding this comment

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

It seems that base_name below does thw work and that if is not needed here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You are right. Removed that if

assert(mapType.values ~= nil, "GraphQL Map type must have 'values' field")
return {
type = "map",
values = gql_type_to_avro(mapType.values),
Copy link
Member

Choose a reason for hiding this comment

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

Double whitespace.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

@SudoBobo SudoBobo force-pushed the union_map_directive_to_avro branch 2 times, most recently from 78b75ed to 5cfae83 Compare August 8, 2018 10:57
Copy link
Member

@Totktonada Totktonada left a comment

Choose a reason for hiding this comment

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

A bit more comments re 'Maps' commit.


local res = core_types.map
local map_name = helpers.full_name("Map", context)
Copy link
Member

Choose a reason for hiding this comment

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

Sorry for nitpicking: single quotes is used in this file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed. Consistency is good

@@ -448,10 +448,11 @@ function types.convert(state, avro_schema, opts)
'got %s (avro_schema %s)'):format(type(avro_schema.values),
json.encode(avro_schema)))

-- validate avro schema format inside 'values'
types.convert(state, avro_schema.values, {context = context})
local converted_values = types.convert(state, avro_schema.values,
Copy link
Member

Choose a reason for hiding this comment

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

Add 'Map' to path before converting and remove after. It does not matter in context of name clashes (I think), but would be consistent with the other converting code: we add type names to path.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, done.

@@ -38,6 +38,9 @@ local function box_type(type_to_box, box_field_name, opts)
-- for the argument type to avoid 'Encountered multiple types' error. See
-- also the comment in @{types.box_collection_type}.
local box_name = (gql_true_type.name or gql_true_type.__type) .. '_box'
if (gql_true_type.__type == "Scalar" and gql_true_type.subtype == 'Map') then
box_name = 'Map_box'
Copy link
Member

Choose a reason for hiding this comment

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

So, please, let short comment here.

@@ -29,7 +30,9 @@ local gql_scalar_to_avro_index = {

local function gql_scalar_to_avro(fieldType)
assert(fieldType.__type == "Scalar", "GraphQL scalar field expected")
assert(fieldType.name ~= "Map", "Map type is not supported")
if fieldType.subtype == "Map" then
Copy link
Member

Choose a reason for hiding this comment

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

Note: here double quotes is used accross the file, so it is okay. The inconsistency btw file is because of different origin authors.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok.

@@ -38,6 +38,9 @@ local function box_type(type_to_box, box_field_name, opts)
-- for the argument type to avoid 'Encountered multiple types' error. See
-- also the comment in @{types.box_collection_type}.
local box_name = (gql_true_type.name or gql_true_type.__type) .. '_box'
if (gql_true_type.__type == "Scalar" and gql_true_type.subtype == 'Map') then
box_name = 'Map_box'
Copy link
Member

Choose a reason for hiding this comment

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

It seems that base_name below does thw work and that if is not needed here.

@SudoBobo SudoBobo force-pushed the union_map_directive_to_avro branch from 5cfae83 to 45f6c0e Compare August 8, 2018 12:05
Copy link
Member

@Totktonada Totktonada left a comment

Choose a reason for hiding this comment

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

Comments for null multi-head connections.

@@ -190,6 +190,25 @@ function resolve.gen_resolve_function_multihead(collection_name, connection,
end

return function(parent, _, info)
-- If parent object does not have all source_fields (for any of
-- variants) non-nill then we do not resolve variant and just return
Copy link
Member

Choose a reason for hiding this comment

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

Typos:

  1. non-nill → non-null
  2. parent object → a parent object
  3. source_fields → source fields (because it is not the name of a field)
  4. started with uppercase letter, but does not end with a period.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you, fixed.

@@ -190,6 +190,25 @@ function resolve.gen_resolve_function_multihead(collection_name, connection,
end

return function(parent, _, info)
-- If parent object does not have all source_fields (for any of
Copy link
Member

Choose a reason for hiding this comment

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

I think we should allow to omit a variant determination only when source fields for all variants are null.

It would be good to have it described explicitly in README. Like so:

Multi-head connections

A parent object is matching against a multi-head connection variants in the order of the variants. The parent object should match with a determinant of at least one variant except the following case. When source fields of all variants are null the multi-head connection obligated to give null object as the result. In this case the parent object is allowed to don’t match any variant. One can use this feature to avoid to set any specific determinant value when a multi-head connection is known to have no connected object.

Ideally, we should have description of the entire multi-head connection feature (but it of course is not part of this set of issues).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you propose to add this short paragraph to README now?

Copy link
Contributor Author

@SudoBobo SudoBobo Aug 9, 2018

Choose a reason for hiding this comment

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

I think we should allow to omit a variant determination only when source fields for all variants are null.

Ok, done.

Copy link
Member

Choose a reason for hiding this comment

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

Yep, to README.

-- variants) non-nill then we do not resolve variant and just return
-- box.NULL
for _, variant in ipairs(c.variants) do
for _, p in ipairs(variant.parts) do
Copy link
Member

Choose a reason for hiding this comment

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

It seems that it would be good to use are_all_parts_null function we already have.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Really not sure. This function has an assertion inside:

assert(ok,
    'FULL MATCH constraint was failed: connection ' ..
    'key parts must be all non-nulls or all nulls; ' ..
    'object: ' .. json.encode(parent))

Consider the case: collection has three fields: 1, 2 and 3. Fields 1 and 2 are used as source fields for connection 1, while fields 2 and 3 are used as source fields for connection 2.
And some collection object has fields 1 and 2 not null and field 3 null. This is ok in general, but would met assertion condition.

Copy link
Contributor Author

@SudoBobo SudoBobo Aug 9, 2018

Choose a reason for hiding this comment

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

After discussion in chat we decided to add opts argument to are_all_parts_null which would suppress assert and use this function.

@@ -0,0 +1,456 @@
local tap = require('tap')
Copy link
Member

Choose a reason for hiding this comment

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

  1. Maybe briefly describe why it is needed and where differs from multihead_conn_testdata.lua? Maybe also it worth to link the issue here.

Copy link
Member

Choose a reason for hiding this comment

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

  1. multihead_conn_testdata_with_nulls.lua → multihead_conn_with_nulls_testdata.lua

Copy link
Contributor Author

@SudoBobo SudoBobo Aug 9, 2018

Choose a reason for hiding this comment

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

  1. Added brief description. Link to the issue seems inappropriate here from my point of view.
  2. Fixed.

end

function multihead_conn_testdata.run_queries(gql_wrapper)
local test = tap.test('multihead_conn_null')
Copy link
Member

@Totktonada Totktonada Aug 8, 2018

Choose a reason for hiding this comment

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

multihead_conn_null → multihead_conn_with_nulls

For the sake of consistency.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

]]):strip())
test:is_deeply(result_1_2.data, exp_result_1_2, '1_2')

local variables_1_3 = {hero_id = 'hero_id_3'}
Copy link
Member

Choose a reason for hiding this comment

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

Please, comment this case and link with the issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added.

@SudoBobo SudoBobo force-pushed the union_map_directive_to_avro branch from 45f6c0e to f918558 Compare August 8, 2018 15:26
Copy link
Member

@Totktonada Totktonada left a comment

Choose a reason for hiding this comment

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

Comments for 'small refactor'. BTW, code style fixes is not a refactorng.

@@ -176,19 +176,19 @@ union_to_avro = function(fieldType, subSelections, context)
if box_sub_selections.selectionSet.selections[1].selectionSet ~= nil then
-- Object GraphQL type case.
type_sub_selections =
box_sub_selections.selectionSet.selections[1].selectionSet.selections
box_sub_selections.selectionSet.selections[1].selectionSet.selections
Copy link
Member

Choose a reason for hiding this comment

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

Don’t break indent. Carry like so:

aaaaa = xxxxxxx.yyyyyyyy
    .zzzzzzz

Copy link
Member

Choose a reason for hiding this comment

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

Can be merged with 'Make small clean up'.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, done.

else
-- Scalar GraphQL type case.
type_sub_selections = box_sub_selections.selectionSet.selections[1]
end

if is_multihead then
local avro_type = gql_type_to_avro(type.kind,
type_sub_selections, context, {is_nullable = true})
type_sub_selections, context, {is_nullable = true})
Copy link
Member

Choose a reason for hiding this comment

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

Your wrote it within the PR in the commit re unions. Merge it into 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.

Done.

table.insert(result.fields, {name = type.name, type = avro_type})
else
table.insert(result, gql_type_to_avro(type.kind,
type_sub_selections, context))
type_sub_selections, context))
Copy link
Member

Choose a reason for hiding this comment

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

Your wrote it within the PR in the commit re unions. Merge it into 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.

Done.

@@ -224,7 +224,7 @@ local function field_to_avro(object_type, fields, context)
assert(d.kind == "directive")
check(d.name.value, "directive.name.value", "string")
assert(d.name.value == "include" or d.name.value == "skip",
"Only 'include' and 'skip' directives are supported for now")
"Only 'include' and 'skip' directives are supported for now")
Copy link
Member

Choose a reason for hiding this comment

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

Your wrote it within the PR in the commit re directives. Merge it into 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.

Done.


-- Converts map recursively into array. If an array with maps is
-- encountered then all this maps will be converted too.
function test_utils.convert_table_to_array(map)
Copy link
Member

Choose a reason for hiding this comment

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

function recursively_map_to_array(t)
    if type(t) == 'table' and not utils.is_array(t) do
        local res = {}
        for k, v in pairs(t) do
            assert(type(k) == 'string')
            table.insert(res, {k, v})
        end
        table.sort(res, function(a, b) return a[1] < b[1] end)
        return res
    end
    return t
end

Copy link
Member

Choose a reason for hiding this comment

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

I don’t test it. Just propose a bit simper variant.

Copy link
Contributor Author

@SudoBobo SudoBobo Aug 8, 2018

Choose a reason for hiding this comment

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

pairs would give us numbers (1, 2, 3) as well, wouldn't it?
So assert(type(k)) == 'string' should fail.
And we may have maps inside arrays as well.

Copy link
Member

Choose a reason for hiding this comment

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

Sorry, forget about recursion. It should like so I guess:

function recursively_map_to_array(t)
    if type(t) == 'table' and not utils.is_array(t) then
        local res = {}
        for k, v in pairs(t) do
            assert(type(k) == 'string')
            table.insert(res, {k, recursively_map_to_array(v)})
        end 
        table.sort(res, function(a, b) return a[1] < b[1] end)
        return res 
    elseif type(t) == 'table' then
        local res = {}
        for _, v in ipairs(t) do
            table.insert(res, recursively_map_to_array(v))
        end 
        return res 
    end 
    return t
end

But as discussed verbally field order in arrays are changed sporadically too and it is hard to simply sort it, because elements can be tables.

Copy link
Member

@Totktonada Totktonada left a comment

Choose a reason for hiding this comment

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

Several months later even you himself will have difficulties with saying for what it was. Comment it in the commit message, add usage in introspection.test.lua.

Copy link
Member

@Totktonada Totktonada left a comment

Choose a reason for hiding this comment

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

Comments re unions.

@@ -97,6 +113,88 @@ map_to_avro = function(mapType)
}
end

--- The function converts a GraphQL Union type to avro-schema type.
Copy link
Member

Choose a reason for hiding this comment

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

'The function' is redundant. Just 'Convert …'.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, fixed.

--- avro-schema records. Each field represents one union variant. Variant type
--- name is taken as a field name. Such records must have all fields nullable.
---
--- We convert multi-head Unions to records instead of unions because in case
Copy link
Member

Choose a reason for hiding this comment

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

multi-head Unions → multi-head connections?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think writing 'multi-head connections' here is a little bit wrong, because we are talking in this comment not about multi-head connections, but about their implementation - Unions.
Changed like this:
multi-head Unions → Unions implementing multi-head connections

Copy link
Member

Choose a reason for hiding this comment

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

We are going to be too formal :) I’m okay.

--- We convert multi-head Unions to records instead of unions because in case
--- of 1:N connections we would not have valid avro-schema (if use unions).
--- This is because according to avro-schema standard unions may not contain
--- more than one schema with the same type (in case of 1:N multi-head
Copy link
Member

Choose a reason for hiding this comment

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

I propose to reuse standard wording here:

Unions may not contain more than one schema with the same type, except for the named types record, fixed and enum.

Note for 'except for' part.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok.

--- of 1:N connections we would not have valid avro-schema (if use unions).
--- This is because according to avro-schema standard unions may not contain
--- more than one schema with the same type (in case of 1:N multi-head
--- connections we would have more than one 'array' in union)
Copy link
Member

Choose a reason for hiding this comment

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

No period at the end.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added.

assert(utils.table_size(box_type.fields) == 1, 'Box Object must ' ..
'have exactly one field')
local type =
box_type.fields[utils.get_keys(box_type.fields)[1]]
Copy link
Member

Choose a reason for hiding this comment

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

local type = select(2, next(box_type.fields))

Copy link
Member

Choose a reason for hiding this comment

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

BTW, I would prefer name field_name.

Copy link
Contributor Author

@SudoBobo SudoBobo Aug 9, 2018

Choose a reason for hiding this comment

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

Fixed with select.
field_name seems to be a wrong naming since type is actually a type. Latter we use type.name and type.kind

Copy link
Member

Choose a reason for hiding this comment

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

Ok.


if is_multihead then
local avro_type = gql_type_to_avro(type.kind,
type_sub_selections, context, {is_nullable = true})
Copy link
Member

Choose a reason for hiding this comment

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

There is avro_helpers.make_avro_type_nullable, so you don’t need to introduce the new option.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, option removed.

assert(fieldType.types ~= nil, "GraphQL Union must have 'types' field")
check(fieldType.types, "fieldType.types", "table")
local is_multihead = (fieldType.resolveType == nil)
local result = {}
Copy link
Member

Choose a reason for hiding this comment

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

In case of is_multihead the table ({}) is always replaced with the new one.

Copy link
Contributor Author

@SudoBobo SudoBobo Aug 9, 2018

Choose a reason for hiding this comment

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

Yes, it was my intention. I thought this previous construction is clearer then:

if is_multihead then
    check(fieldType.name, "fieldType.name", "string")
    result = {
        type = 'record',
        name = fieldType.name,
        fields = {}
    }
else
    result = {}
end

Copy link
Member

Choose a reason for hiding this comment

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

Maybe it is clearer in some way, maybe doesn’t. I would prefer to avoid it, because don’t sure how such empty objects can affect gc.

type_sub_selections, context, {is_nullable = true})
table.insert(result.fields, {name = type.name, type = avro_type})
else
table.insert(result, gql_type_to_avro(type.kind,
Copy link
Member

Choose a reason for hiding this comment

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

We need to add 'null' depending of whether the GraphQL Union is nullable. I think it is already handled somewhere with avro_helpers.make_avro_type_nullable. Is it so?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is. We do it in gql_type_to_avro() before returning result in case of 'natural' Unions.

Copy link
Member

Choose a reason for hiding this comment

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

Seems to be correct. Thanks.

local meta = testdata.get_test_metadata()
testdata.fill_test_data(box.space, meta)

local accessor = graphql.accessor_space.new({
Copy link
Member

Choose a reason for hiding this comment

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

Better to use just graphql.new, I want to deprecate explicit use of built-in accessors at some time.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok.

]]

local expected_avro_schema = [[
type: record
Copy link
Member

Choose a reason for hiding this comment

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

Indent.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

Copy link
Member

@Totktonada Totktonada left a comment

Choose a reason for hiding this comment

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

Comments for directives support.

The test is really huge, thanks!

assert(d.name.value == "include" or d.name.value == "skip",
"Only 'include' and 'skip' directives are supported for now")
end
if type(fieldTypeAvro) == "string" then
Copy link
Member

Choose a reason for hiding this comment

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

Just use avro_helpers.make_avro_type_nullable.

local yaml = require('yaml')
local avro = require('avro_schema')
local test = require('tap').test('to avro schema')
local common_testdata = require('test.testdata.common_testdata')
Copy link
Member

Choose a reason for hiding this comment

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

Place relative imports after package.path updating.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok.

]]

local expected_avro_schema = [[
type: record
Copy link
Member

Choose a reason for hiding this comment

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

Please, add indent.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, done.

user_id = 'user_id_1',
include_description = false,
skip_discount = true,
include_user = true,
Copy link
Member

Choose a reason for hiding this comment

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

Are you intend to invert boolean values comparing to variables_1? If so, you missed include_user.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, there was no such intention. I wanted to test nested directives. Like this:

user_connection(user_id: "user_id_1") @include(if: $include_user) {
    user_id
    first_name @include(if: $include_user_first_name)
}

Copy link
Member

Choose a reason for hiding this comment

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

Okay.

]]

local expected_avro_schema = [[
type: record
Copy link
Member

Choose a reason for hiding this comment

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

The same as above: indent.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fxd.


local ok, err = avro.validate(compiled_schema, result_2)
assert(ok, tostring(err))
test:is(ok, true, 'query response validation by avro - union 2')
Copy link
Member

Choose a reason for hiding this comment

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

test:ok

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fxd.

]])

test:is_deeply(result_2, result_expected_2,
'comparision between expected and actual query response - multihead 2')
Copy link
Member

Choose a reason for hiding this comment

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

Indent.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fxd.

local union_meta = union_testdata.get_test_metadata()
union_testdata.fill_test_data(box.space, union_meta)

local accessor = graphql.accessor_space.new({
Copy link
Member

Choose a reason for hiding this comment

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

The same as above: please, use graphql.new w/o separate accessor creating.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fxd.

local multihead_meta = multihead_testdata.get_test_metadata()
multihead_testdata.fill_test_data(box.space, multihead_meta)

local accessor = graphql.accessor_space.new({
Copy link
Member

Choose a reason for hiding this comment

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

The same: proposed to move accessor’s arguments to the graphql.new call.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fxd.

local common_meta = common_testdata.get_test_metadata()
common_testdata.fill_test_data(box.space, common_meta)

local accessor = graphql.accessor_space.new({
Copy link
Member

Choose a reason for hiding this comment

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

I think it is better to use graphql.new in new tests and maybe deprecate separate usage of build-in accessors in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fxd.

@SudoBobo SudoBobo force-pushed the union_map_directive_to_avro branch from f918558 to 2f7dafc Compare August 9, 2018 17:11
@SudoBobo SudoBobo force-pushed the union_map_directive_to_avro branch from 2f7dafc to 312b47c Compare August 9, 2018 18:05
Copy link
Member

@Totktonada Totktonada left a comment

Choose a reason for hiding this comment

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

LGTM.

Some minor comments are below.

hero_type: human
hero_connection:
human_collection:
name: Luke
Copy link
Member

Choose a reason for hiding this comment

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

The indent for yaml seems to be 2 spaces everywhere (because of yaml.encode dump it so), but here there are several places with 4 spaces indent.

Copy link
Contributor Author

@SudoBobo SudoBobo Aug 10, 2018

Choose a reason for hiding this comment

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

Fixed.

testdata.fill_test_data()
local meta = testdata.get_test_metadata()

local accessor = graphql.accessor_space.new({
Copy link
Member

Choose a reason for hiding this comment

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

Proposed to move the options to graphql.new.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

]]

local expected_avro_schema = [[
type: record
Copy link
Member

Choose a reason for hiding this comment

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

Indent.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

banking_type: credit
hero_banking_connection:
credit_account_collection:
- account_id: credit_account_id_1
Copy link
Member

Choose a reason for hiding this comment

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

4 spaces indent instead of 2 spaces.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

banking_type: dublon
hero_banking_connection:
dublon_account_collection:
- account_id: dublon_account_id_1
Copy link
Member

Choose a reason for hiding this comment

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

4 spaces indent instead of 2.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

@SudoBobo SudoBobo force-pushed the union_map_directive_to_avro branch from 312b47c to 286cb8d Compare August 10, 2018 15:56
@SudoBobo SudoBobo merged commit 040b92b into master Aug 10, 2018
@Totktonada Totktonada deleted the union_map_directive_to_avro branch August 10, 2018 21:27
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants