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

Commit 79d227a

Browse files
committed
Refactor arguments generation / converting
* Generate object arguments in avro-schema first. * Generate object arguments for subrecords. * Use full path for a GraphQL type name where possible. * Part of #166. * Part of #73 (2nd bullet). * Fixes #163. * Fixes #46.
1 parent fb4b75e commit 79d227a

23 files changed

+877
-396
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@ doc/apidoc
1919

2020
# benchmarks results
2121
bench.*.result.txt
22+
23+
# temporary directory for make apidoc-lint
24+
doc/apidoc-lint-tmp

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,5 @@ install:
6060
- sudo pip install virtualenv
6161

6262
script:
63+
- make apidoc-lint
6364
- make test

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ pure-bench:
4545
clean:
4646
rm -rf test/var
4747

48+
.PHONY: apidoc-lint
49+
apidoc-lint:
50+
! ldoc -d doc/apidoc-lint-tmp graphql --all -f markdown 2>&1 >/dev/null | \
51+
grep -v ': no module() call found; no initial doc comment$$\|: contains no items$$'
52+
rm -rf doc/apidoc-lint-tmp
53+
4854
.PHONY: apidoc
4955
apidoc:
5056
ldoc -d doc/apidoc graphql --all -f markdown

graphql/accessor_general.lua

Lines changed: 42 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ end
291291
--- (directly or indirectly using the `accessor_space.new` or the
292292
--- `accessor_shard.new` function); this function uses the
293293
--- `self.index_cache` prebuild table representing available indexes
294-
294+
---
295295
--- @tparam string collection_name name of a collection of whose indexes the
296296
--- function will search through
297297
---
@@ -332,7 +332,7 @@ end
332332
--- `nil`, or contains `value_list` field to pass to a GT (great-then) index,
333333
--- or contains `filter` field to use in `process_tuple` for find the pivot in
334334
--- a select result
335-
local get_index_name = function(self, collection_name, from, filter, args)
335+
local function get_index_name(self, collection_name, from, filter, args)
336336
assert(type(self) == 'table',
337337
'self must be a table, got ' .. type(self))
338338
assert(type(collection_name) == 'string',
@@ -772,36 +772,42 @@ end
772772
local function match_using_re(obj, pcre)
773773
if pcre == nil then return true end
774774

775+
assert(rex ~= nil, 'we should not pass over :compile() ' ..
776+
'with a query contains PCRE matching when there are '..
777+
'no lrexlib-pcre (rex_pcre) module present')
778+
775779
for field_name, re in pairs(pcre) do
776780
-- skip an object with null in a string* field
777781
if obj[field_name] == nil then
778782
return false
779783
end
780-
assert(rex ~= nil, 'we should not pass over :compile() ' ..
781-
'with a query contains PCRE matching when there are '..
782-
'no lrexlib-pcre (rex_pcre) module present')
783-
local flags = rex.flags()
784-
-- emulate behaviour of (?i) on libpcre (libpcre2 supports it)
785-
local cfg = 0
786-
if not is_pcre2 then
787-
local cnt
788-
re, cnt = re:gsub('^%(%?i%)', '')
789-
if cnt > 0 then
790-
cfg = bit.bor(cfg, flags.CASELESS)
791-
end
792-
end
793-
-- enable UTF-8
794-
if is_pcre2 then
795-
cfg = bit.bor(cfg, flags.UTF)
796-
cfg = bit.bor(cfg, flags.UCP)
784+
if type(re) == 'table' then
785+
local match = match_using_re(obj[field_name], re)
786+
if not match then return false end
797787
else
798-
cfg = bit.bor(cfg, flags.UTF8)
799-
cfg = bit.bor(cfg, flags.UCP)
800-
end
801-
-- XXX: compile re once
802-
local re = rex.new(re, cfg)
803-
if not re:match(obj[field_name]) then
804-
return false
788+
local flags = rex.flags()
789+
-- emulate behaviour of (?i) on libpcre (libpcre2 supports it)
790+
local cfg = 0
791+
if not is_pcre2 then
792+
local cnt
793+
re, cnt = re:gsub('^%(%?i%)', '')
794+
if cnt > 0 then
795+
cfg = bit.bor(cfg, flags.CASELESS)
796+
end
797+
end
798+
-- enable UTF-8
799+
if is_pcre2 then
800+
cfg = bit.bor(cfg, flags.UTF)
801+
cfg = bit.bor(cfg, flags.UCP)
802+
else
803+
cfg = bit.bor(cfg, flags.UTF8)
804+
cfg = bit.bor(cfg, flags.UCP)
805+
end
806+
-- XXX: compile re once
807+
local re = rex.new(re, cfg)
808+
if not re:match(obj[field_name]) then
809+
return false
810+
end
805811
end
806812
end
807813

@@ -908,23 +914,6 @@ local function process_tuple(state, tuple, opts)
908914
return true
909915
end
910916

911-
--- Get schema name by a collection name.
912-
---
913-
--- @tparam table self data accessor instance
914-
---
915-
--- @tparam string collection_name
916-
---
917-
--- @treturn string `schema_name`
918-
local function get_schema_name(self, collection_name)
919-
local collection = self.collections[collection_name]
920-
assert(collection ~= nil,
921-
('cannot find the collection "%s"'):format(collection_name))
922-
local schema_name = collection.schema_name
923-
assert(type(schema_name) == 'string',
924-
'schema_name must be a string, got ' .. type(schema_name))
925-
return schema_name
926-
end
927-
928917
--- Call one of accessor function: `update_tuple` or `delete_tuple` for each
929918
--- selected object.
930919
---
@@ -1141,7 +1130,7 @@ local function insert_internal(self, collection_name, from, filter, args, extra)
11411130
check(from.collection_name, 'from.collection_name', 'nil')
11421131

11431132
-- convert object -> tuple (set default values from a schema)
1144-
local schema_name = get_schema_name(self, collection_name)
1133+
local schema_name = db_schema_helpers.get_schema_name(self, collection_name)
11451134
local default_flatten_object = self.default_flatten_object[schema_name]
11461135
assert(default_flatten_object ~= nil,
11471136
('cannot find default_flatten_object ' ..
@@ -1183,7 +1172,7 @@ local function update_internal(self, collection_name, extra, selected)
11831172
assert(next(extra.extra_args, next(extra.extra_args)) == nil, err_msg)
11841173

11851174
-- convert xobject -> update statements
1186-
local schema_name = get_schema_name(self, collection_name)
1175+
local schema_name = db_schema_helpers.get_schema_name(self, collection_name)
11871176
local default_xflatten = self.default_xflatten[schema_name]
11881177
assert(default_xflatten ~= nil,
11891178
('cannot find default_xflatten ' ..
@@ -1199,10 +1188,16 @@ end
11991188

12001189
--- Delete an object.
12011190
---
1202-
--- Parameters are the same as for @{select_internal}.
1191+
--- Corresponding parameters are the same as for @{select_internal}.
1192+
---
1193+
--- @tparam table self
1194+
---
1195+
--- @tparam string collection_name
12031196
---
12041197
--- @tparam table extra `extra.extra_args.delete` is used
12051198
---
1199+
--- @tparam table selected objects to delete
1200+
---
12061201
--- @treturn table `new_objects` list of deleted objects (in the order of the
12071202
--- `selected` parameter)
12081203
local function delete_internal(self, collection_name, extra, selected)
@@ -1212,7 +1207,7 @@ local function delete_internal(self, collection_name, extra, selected)
12121207
'arguments'
12131208
assert(next(extra.extra_args, next(extra.extra_args)) == nil, err_msg)
12141209

1215-
local schema_name = get_schema_name(self, collection_name)
1210+
local schema_name = db_schema_helpers.get_schema_name(self, collection_name)
12161211

12171212
return perform_primary_key_operation(self, collection_name, schema_name,
12181213
selected, 'delete_tuple')

graphql/avro_helpers.lua

Lines changed: 101 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ local avro_helpers = {}
2828
---
2929
--- * `raise_on_nullable` (boolean) raise an error on nullable type
3030
---
31-
--- @result `result` (string or table) nullable avro type
31+
--- @return `result` (string or table) nullable avro type
3232
function avro_helpers.make_avro_type_nullable(avro, opts)
3333
assert(avro ~= nil, "avro must not be nil")
3434
local opts = opts or {}
@@ -69,7 +69,8 @@ function avro_helpers.make_avro_type_nullable(avro, opts)
6969
end
7070
return avro
7171
elseif value_type == 'table' and #avro == 0 then
72-
return avro_helpers.make_avro_type_nullable(avro.type, opts)
72+
avro.type = avro_helpers.make_avro_type_nullable(avro.type, opts)
73+
return avro
7374
end
7475

7576
error("avro should be a string or a table, got " .. value_type)
@@ -108,24 +109,6 @@ function avro_helpers.is_scalar_type(avro_schema_type)
108109
return scalar_types[avro_schema_type] or false
109110
end
110111

111-
function avro_helpers.is_comparable_scalar_type(avro_schema_type)
112-
check(avro_schema_type, 'avro_schema_type', 'string')
113-
114-
local scalar_types = {
115-
['int'] = true,
116-
['int*'] = true,
117-
['long'] = true,
118-
['long*'] = true,
119-
['boolean'] = true,
120-
['boolean*'] = true,
121-
['string'] = true,
122-
['string*'] = true,
123-
['null'] = true,
124-
}
125-
126-
return scalar_types[avro_schema_type] or false
127-
end
128-
129112
function avro_helpers.is_compound_type(avro_schema_type)
130113
check(avro_schema_type, 'avro_schema_type', 'string')
131114

@@ -136,30 +119,123 @@ function avro_helpers.is_compound_type(avro_schema_type)
136119
['array*'] = true,
137120
['map'] = true,
138121
['map*'] = true,
122+
['union'] = true,
139123
}
140124

141125
return compound_types[avro_schema_type] or false
142126
end
143127

128+
--- Get type of an avro-schema.
129+
---
130+
--- @param avro_schema (table or string) input avro-schema
131+
---
132+
--- @tparam[opt] table opts the following options:
133+
---
134+
--- * allow_references (boolean)
135+
---
136+
--- @treturn string `avro_t` type of the avro-schema
137+
---
138+
--- @treturn boolean `is_ref` whether the avro-schema is reference to another
139+
--- avro-schema type
144140
function avro_helpers.avro_type(avro_schema, opts)
145141
local opts = opts or {}
146142
local allow_references = opts.allow_references or false
147143

148144
if type(avro_schema) == 'table' then
149145
if utils.is_array(avro_schema) then
150-
return 'union'
146+
return 'union', false
151147
elseif avro_helpers.is_compound_type(avro_schema.type) then
152-
return avro_schema.type
148+
return avro_schema.type, false
153149
elseif allow_references then
154-
return avro_schema
150+
return avro_schema, true
155151
end
156152
elseif type(avro_schema) == 'string' then
157153
if avro_helpers.is_scalar_type(avro_schema) then
158-
return avro_schema
154+
return avro_schema, false
159155
elseif allow_references then
160-
return avro_schema
156+
return avro_schema, true
157+
end
158+
end
159+
160+
error('unrecognized avro-schema type: ' .. json.encode(avro_schema))
161+
end
162+
163+
--- Expand avro-schema references.
164+
---
165+
--- @param avro_schema (table or string) input avro-schema
166+
---
167+
--- @tparam[opt] table opts the following options:
168+
---
169+
--- * definitions (table) processed avro-schemas to expand further references
170+
---
171+
--- @return generated expanded avro-schema
172+
function avro_helpers.expand_references(avro_schema, opts)
173+
local opts = opts or {}
174+
local definitions = opts.definitions or {}
175+
176+
local avro_t, is_ref = avro_helpers.avro_type(avro_schema,
177+
{allow_references = true})
178+
179+
if is_ref then
180+
assert(definitions[avro_t] ~= nil,
181+
('undefined reference: %s'):format(avro_t))
182+
return definitions[avro_t]
183+
elseif avro_t == 'union' then
184+
local res = {}
185+
for _, child in ipairs(avro_schema) do
186+
table.insert(res, avro_helpers.expand_references(child,
187+
{definitions = definitions}))
161188
end
189+
return res
190+
elseif avro_t == 'record' or avro_t == 'record*' then
191+
local res = table.copy(avro_schema)
192+
res.fields = {}
193+
194+
local res_nonnull
195+
local res_nullable
196+
if avro_t == 'record' then
197+
res_nonnull = res
198+
res_nullable = table.copy(res)
199+
res_nullable.type = 'record*'
200+
res_nullable.fields = res.fields
201+
else
202+
res_nonnull = table.copy(res)
203+
res_nonnull.type = 'record'
204+
res_nonnull.fields = res.fields
205+
res_nullable = res
206+
end
207+
208+
-- Saving type before traverse deeper allows to use reference to it
209+
-- inside (it is allowed by our avro-schema implementation for nullable
210+
-- fields, union, array and map).
211+
local name = avro_schema.name
212+
assert(definitions[name] == nil and definitions[name .. '*'] == nil,
213+
('multiple definitions of %s'):format(name))
214+
definitions[name] = res_nonnull
215+
definitions[name .. '*'] = res_nullable
216+
217+
for _, field in ipairs(avro_schema.fields) do
218+
local field = table.copy(field)
219+
field.type = avro_helpers.expand_references(field.type,
220+
{definitions = definitions})
221+
table.insert(res.fields, field)
222+
end
223+
224+
return res
225+
elseif avro_t == 'array' or avro_t == 'array*' then
226+
local res = table.copy(avro_schema)
227+
res.items = avro_helpers.expand_references(avro_schema.items,
228+
{definitons = definitions})
229+
return res
230+
elseif avro_t == 'map' or avro_t == 'map*' then
231+
local res = table.copy(avro_schema)
232+
res.values = avro_helpers.expand_references(avro_schema.values,
233+
{definitons = definitions})
234+
return res
235+
elseif avro_helpers.is_scalar_type(avro_t) then
236+
return avro_schema
162237
end
238+
163239
error('unrecognized avro-schema type: ' .. json.encode(avro_schema))
164240
end
165241

graphql/config_complement.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ end
203203
--- @tparam table connections cfg.connections - user-defined collections
204204
--- @tparam table indexes cfg.indexes - {[collection_name] = collection_indexes, ...}
205205
--- @treturn table `collections` is complemented collections
206-
local function complement_connections(collections, connections, indexes, schemas)
206+
local function complement_connections(collections, connections, indexes)
207207
if connections == nil then
208208
return collections
209209
end

0 commit comments

Comments
 (0)