|
| 1 | +local utils = require('graphql.utils') |
| 2 | +local at = utils.assert_type |
| 3 | + |
| 4 | +local config_complement = {} |
| 5 | + |
| 6 | +--- The function determines connection type by fully qualified connection.parts |
| 7 | +--- and source collection space format |
| 8 | +--- Items in parts must be sorted by destination_field to match field order |
| 9 | +--- in connection's index |
| 10 | +local function determine_connection_type(connection_parts, index, source_format) |
| 11 | + local type |
| 12 | + |
| 13 | + if #connection_parts < #(index.fields) then |
| 14 | + type = '1:N' |
| 15 | + end |
| 16 | + |
| 17 | + if #connection_parts == #(index.fields) then |
| 18 | + if index.unique then |
| 19 | + type = '1:1' |
| 20 | + else |
| 21 | + type = '1:N' |
| 22 | + end |
| 23 | + end |
| 24 | + |
| 25 | + -- todo still not clear how to handle nullables |
| 26 | + return type |
| 27 | +end |
| 28 | + |
| 29 | +--- XXX Currently if connections_parts have destination_fields that do not |
| 30 | +--- exist in connection's index the assertion is raised. Maybe it is not a |
| 31 | +--- desirable behavior. Consider the following case: you have working |
| 32 | +--- tarantool_graphql, then you try to change configuration a bit and |
| 33 | +--- tarantool_graphql crushes. |
| 34 | +local function sort_parts(connection_parts, index_fields) |
| 35 | + local sorted_parts = {} |
| 36 | + for _, part in pairs(connection_parts) do |
| 37 | + local is_found = false |
| 38 | + for i, index_field in ipairs(index_fields) do |
| 39 | + if part.destination_field == index_field then |
| 40 | + sorted_parts[i] = part |
| 41 | + is_found = true |
| 42 | + end |
| 43 | + end |
| 44 | + assert(is_found, ('part.destination_field %s was not found in ' .. |
| 45 | + 'connection index'):format(part.destination_field)) |
| 46 | + end |
| 47 | + return sorted_parts; |
| 48 | +end |
| 49 | + |
| 50 | +--- The functions determines fully qualified connection's parts based on |
| 51 | +--- connection's index and incompletely qualified connection's part given by user |
| 52 | +--- @tparam table parts incompletely qualified connection's part given by user |
| 53 | +--- @tparam table index connection index (cfg.indexes[collection][index_name] |
| 54 | +--- order of items in index.fields must be the same as in index format |
| 55 | +local function determine_connection_parts(parts, index) |
| 56 | + at(parts, 'parts', 'nil', 'number', 'table') |
| 57 | + local result_parts = {} |
| 58 | + |
| 59 | + -- full index case |
| 60 | + if type(parts) == 'nil' then |
| 61 | + for i, v in ipairs(index.fields) do |
| 62 | + result_parts[i] = {source_field = v, destination_field = v} |
| 63 | + end |
| 64 | + end |
| 65 | + |
| 66 | + -- index prefix case |
| 67 | + if type(parts) == 'number' then |
| 68 | + for i = 1, parts do |
| 69 | + local v = index.fields[i] |
| 70 | + result_parts[i] = {source_field = v, destination_field = v} |
| 71 | + end |
| 72 | + end |
| 73 | + |
| 74 | + -- fully qualified parts case |
| 75 | + if type(parts) == 'table' then |
| 76 | + local sorted_parts = sort_parts(parts, index.fields) |
| 77 | + for i, v in ipairs(sorted_parts) do |
| 78 | + result_parts[i] = {source_field = v, destination_field = v} |
| 79 | + end |
| 80 | + end |
| 81 | + |
| 82 | + return result_parts |
| 83 | +end |
| 84 | + |
| 85 | +--- |
| 86 | +--- The function complements collections' connections, described in simplified |
| 87 | +--- format, to connections in a fully specified format. Connection's |
| 88 | +--- collection determined on source_collection. Type determined on index type. |
| 89 | +--- Notice an example: |
| 90 | +--- |
| 91 | +--- "connections" : [ |
| 92 | +--- { |
| 93 | +--- "name": "order_connection", |
| 94 | +--- "source_collection": "user_collection", |
| 95 | +--- "destination_collection": "order_collection" |
| 96 | +--- "index_name": "user_id_index", |
| 97 | +--- "parts" : nil | number | table (destination fields can be omitted) |
| 98 | +--- }, |
| 99 | +--- ... |
| 100 | +--- ] |
| 101 | +--- will produce following complement in 'user_collection' : |
| 102 | +--- |
| 103 | +--- "user_collection": { |
| 104 | +--- "schema_name": "user", |
| 105 | +--- "connections": [ |
| 106 | +--- { |
| 107 | +--- "type": "1:N", |
| 108 | +--- "name": "order_connection", |
| 109 | +--- "destination_collection": "order_collection", |
| 110 | +--- "parts": [ |
| 111 | +--- { "source_field": "user_id", "destination_field": "user_id" } |
| 112 | +--- ], |
| 113 | +--- "index_name": "user_id_index" |
| 114 | +--- }, |
| 115 | +--- |
| 116 | +--- ] |
| 117 | +--- } |
| 118 | +--- |
| 119 | +--- @tparam table collections cfg.collections |
| 120 | +--- @tparam table connections cfg.connections - user-defined collections |
| 121 | +--- @tparam table indexes cfg.indexes - {collection_name = collection_indexes} |
| 122 | +local function complement_connections(collections, connections, indexes, schemas) |
| 123 | + if connections == nil then |
| 124 | + return collections |
| 125 | + end |
| 126 | + |
| 127 | + at(collections, 'collections', 'table') |
| 128 | + at(connections, 'connections', 'table') |
| 129 | + |
| 130 | + for _, c in pairs(connections) do |
| 131 | + at(c.name, 'connection.name', 'string') |
| 132 | + at(c.source_collection, 'connection.source_collection', 'string') |
| 133 | + at(c.destination_collection, 'connection.destination_collection', |
| 134 | + 'string') |
| 135 | + at(c.index_name, 'connection.index_name', 'string') |
| 136 | + at(c.parts, 'connection.parts', 'string', 'table', 'nil') |
| 137 | + |
| 138 | + local index = indexes[c.source_collection][c.index_name] |
| 139 | + assert(index.unique ~= nil, 'index.unique must not be nil ' .. |
| 140 | + 'during connections complementing') |
| 141 | + |
| 142 | + local result_c = {} |
| 143 | + result_c.parts = determine_connection_parts(c.parts, index) |
| 144 | + result_c.type = determine_connection_type(result_c.parts, index) |
| 145 | + result_c.index_name = c.index_name |
| 146 | + result_c.destination_collection = c.destination_collection |
| 147 | + result_c.name = c.name |
| 148 | + |
| 149 | + local collection_connections = collections[c.source_collection]. |
| 150 | + connections |
| 151 | + if collection_connections == nil then |
| 152 | + collection_connections = {} |
| 153 | + end |
| 154 | + collection_connections[#collection_connections + 1] = result_c |
| 155 | + end |
| 156 | + |
| 157 | + return collections |
| 158 | +end |
| 159 | + |
| 160 | +--- The function complements |
| 161 | +function config_complement.complement_cfg(cfg) |
| 162 | + cfg.collections = complement_connections(cfg.collections, cfg.connections, |
| 163 | + cfg.indexes) |
| 164 | + return cfg |
| 165 | +end |
| 166 | + |
| 167 | +return config_complement |
0 commit comments