blob: e47a276a80680e51da56fcec872db95cb7e823fb [file] [log] [blame]
--
-- Licensed to the Apache Software Foundation (ASF) under one or more
-- contributor license agreements. See the NOTICE file distributed with
-- this work for additional information regarding copyright ownership.
-- The ASF licenses this file to You under the Apache License, Version 2.0
-- (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
local core = require("apisix.core")
local gq_parse = require("graphql").parse
local req_set_body_data = ngx.req.set_body_data
local ipairs = ipairs
local pcall = pcall
local type = type
local schema = {
type = "object",
properties = {
query = {
type = "string",
minLength = 1,
maxLength = 1024,
},
variables = {
type = "array",
items = {
type = "string"
},
minItems = 1,
},
operation_name = {
type = "string",
minLength = 1,
maxLength = 1024
},
},
required = {"query"},
}
local plugin_name = "degraphql"
local _M = {
version = 0.1,
priority = 509,
name = plugin_name,
schema = schema,
}
function _M.check_schema(conf)
local ok, err = core.schema.check(schema, conf)
if not ok then
return false, err
end
local ok, res = pcall(gq_parse, conf.query)
if not ok then
return false, "failed to parse query: " .. res
end
if #res.definitions > 1 and not conf.operation_name then
return false, "operation_name is required if multiple operations are present in the query"
end
return true
end
local function fetch_post_variables(conf)
local req_body, err = core.request.get_body()
if err ~= nil then
core.log.error("failed to get request body: ", err)
return nil, 503
end
if not req_body then
core.log.error("missing request body")
return nil, 400
end
-- JSON as the default content type
req_body, err = core.json.decode(req_body)
if type(req_body) ~= "table" then
core.log.error("invalid request body can't be decoded: ", err or "bad type")
return nil, 400
end
local variables = {}
for _, v in ipairs(conf.variables) do
variables[v] = req_body[v]
end
return variables
end
local function fetch_get_variables(conf)
local args = core.request.get_uri_args()
local variables = {}
for _, v in ipairs(conf.variables) do
variables[v] = args[v]
end
return variables
end
function _M.access(conf, ctx)
local meth = core.request.get_method()
if meth ~= "POST" and meth ~= "GET" then
return 405
end
local new_body = core.table.new(0, 3)
if conf.variables then
local variables, code
if meth == "POST" then
variables, code = fetch_post_variables(conf)
else
variables, code = fetch_get_variables(conf)
end
if not variables then
return code
end
if meth == "POST" then
new_body["variables"] = variables
else
new_body["variables"] = core.json.encode(variables)
end
end
new_body["operationName"] = conf.operation_name
new_body["query"] = conf.query
if meth == "POST" then
if not conf.variables then
-- the set_body_data requires to read the body first
core.request.get_body()
end
core.request.set_header(ctx, "Content-Type", "application/json")
req_set_body_data(core.json.encode(new_body))
else
core.request.set_uri_args(ctx, new_body)
end
end
return _M