Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
64e6129
feat: add query gateway
fatihbm Aug 11, 2026
dd000f1
feat: add query gateway
fatihbm Aug 11, 2026
f171472
feat: add query gateway
fatihbm Aug 11, 2026
532d19d
feat: add query gateway
fatihbm Aug 11, 2026
485ba39
feat: register query gateway
fatihbm Aug 11, 2026
f349dea
feat: install query gateway
fatihbm Aug 11, 2026
e9507dc
docs: index query gateway
fatihbm Aug 11, 2026
f05da0a
test: list query gateway plugin
fatihbm Aug 11, 2026
16303a2
test: order query gateway in plugin list
fatihbm Aug 11, 2026
1f65bc8
test: define query gateway echo upstream
fatihbm Aug 11, 2026
025742e
fix: stabilize query gateway cache input
fatihbm Aug 11, 2026
91903fa
fix: stabilize query gateway cache key
fatihbm Aug 11, 2026
66f189e
fix: preserve query gateway cache hit status
fatihbm Aug 11, 2026
9200ce2
fix: skip query gateway filters on cache hit
fatihbm Aug 11, 2026
310e271
test: add query gateway Elasticsearch integration profile
fatihbm Aug 11, 2026
8fca61d
test: add query gateway Elasticsearch integration profile
fatihbm Aug 11, 2026
44a2c8d
test: add query gateway Elasticsearch integration profile
fatihbm Aug 11, 2026
a8fe371
test: add query gateway Elasticsearch integration profile
fatihbm Aug 11, 2026
b911cea
test: add query gateway Elasticsearch integration profile
fatihbm Aug 11, 2026
f1c4a1a
test: add query gateway Elasticsearch integration profile
fatihbm Aug 11, 2026
c3b89cc
test: configure Elasticsearch body tracing
fatihbm Aug 11, 2026
202f671
test: wait for query gateway listener
fatihbm Aug 11, 2026
d42ebb4
ci: add query gateway Elasticsearch integration workflow
fatihbm Aug 11, 2026
2e193a7
test: add UI build context placeholder
fatihbm Aug 11, 2026
5ab6628
test: cover POST query gateway integration
fatihbm Aug 11, 2026
3b8f8db
test: expand query gateway integration matrix
fatihbm Aug 11, 2026
17bca4b
test: emit traceable APISIX access logs
fatihbm Aug 11, 2026
659daf9
test: assert integration trace correlation
fatihbm Aug 11, 2026
c841c42
docs: document query gateway integration coverage
fatihbm Aug 11, 2026
e6eda74
test: allow integration client to write artifacts
fatihbm Aug 11, 2026
0daefe4
test: fail integration on client errors
fatihbm Aug 11, 2026
2ef0450
test: collect integration logs on failure
fatihbm Aug 11, 2026
ac392f5
test: configure query gateway cache shared dict
fatihbm Aug 11, 2026
314693c
test: declare integration shared dictionaries in nginx
fatihbm Aug 11, 2026
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
48 changes: 48 additions & 0 deletions .github/workflows/query-gateway-elasticsearch-integration.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#
# 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.
#

name: Query Gateway Elasticsearch Integration

on:
pull_request:
paths:
- apisix/plugins/query-gateway/**
- apisix/cli/config.lua
- t/integration/query-gateway-elasticsearch/**
workflow_dispatch:

permissions:
contents: read

jobs:
query-gateway-elasticsearch:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
submodules: recursive
- name: Run query gateway Elasticsearch integration profile
working-directory: t/integration/query-gateway-elasticsearch
run: make run
- name: Upload integration artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: query-gateway-elasticsearch-artifacts
path: t/integration/query-gateway-elasticsearch/artifacts
if-no-files-found: warn
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,9 @@ install: runtime
$(ENV_INSTALL) -d $(ENV_INST_LUADIR)/apisix/plugins/proxy-cache
$(ENV_INSTALL) apisix/plugins/proxy-cache/*.lua $(ENV_INST_LUADIR)/apisix/plugins/proxy-cache/

$(ENV_INSTALL) -d $(ENV_INST_LUADIR)/apisix/plugins/query-gateway
$(ENV_INSTALL) apisix/plugins/query-gateway/*.lua $(ENV_INST_LUADIR)/apisix/plugins/query-gateway/

$(ENV_INSTALL) -d $(ENV_INST_LUADIR)/apisix/plugins/serverless
$(ENV_INSTALL) apisix/plugins/serverless/*.lua $(ENV_INST_LUADIR)/apisix/plugins/serverless/

Expand Down
3 changes: 3 additions & 0 deletions apisix/cli/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ local _M = {
["plugin-limit-count"] = "10m",
["prometheus-metrics"] = "128m",
["plugin-limit-conn"] = "10m",
["query-gateway-cache"] = "32m",
["query-gateway-redis-cluster-slot-lock"] = "1m",
["worker-events"] = "10m",
["lrucache-lock"] = "10m",
["balancer-ewma"] = "10m",
Expand Down Expand Up @@ -254,6 +256,7 @@ local _M = {
"proxy-mirror",
"graphql-proxy-cache",
"proxy-rewrite",
"query-gateway",
"workflow",
"api-breaker",
"graphql-limit-count",
Expand Down
186 changes: 186 additions & 0 deletions apisix/plugins/query-gateway.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
--
-- 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 cache = require("apisix.plugins.query-gateway.cache")
local core = require("apisix.core")
local ngx = ngx

local plugin_name = "query-gateway"

local cache_schema = {
type = "object",
properties = {
enabled = {type = "boolean", default = false},
backend = {
type = "string",
enum = {"local", "redis", "redis-cluster"},
default = "local",
},
ttl = {type = "integer", minimum = 1, default = 30},
fallback_ttl = {type = "integer", minimum = 1, default = 5},
max_request_body_size = {type = "integer", minimum = 1, default = 262144},
max_response_body_size = {type = "integer", minimum = 1, default = 1048576},
cookie_names = {
type = "array",
uniqueItems = true,
items = {type = "string", minLength = 1, maxLength = 256},
},
redis_host = {type = "string", minLength = 1},
redis_port = {type = "integer", minimum = 1, maximum = 65535, default = 6379},
redis_timeout = {type = "integer", minimum = 1, default = 1000},
redis_username = {type = "string", minLength = 1},
redis_password = {type = "string", minLength = 1},
redis_database = {type = "integer", minimum = 0, default = 0},
redis_ssl = {type = "boolean", default = false},
redis_ssl_verify = {type = "boolean", default = false},
redis_keepalive_timeout = {type = "integer", minimum = 1, default = 10000},
redis_keepalive_pool = {type = "integer", minimum = 1, default = 100},
redis_cluster_name = {type = "string", minLength = 1},
redis_cluster_nodes = {
type = "array",
minItems = 1,
items = {type = "string", minLength = 1},
},
redis_cluster_ssl = {type = "boolean", default = false},
redis_cluster_ssl_verify = {type = "boolean", default = false},
},
additionalProperties = false,
}

local schema = {
type = "object",
properties = {
preserve_original_method_header = {
description = "whether to forward the original request method",
type = "boolean",
default = true,
},
original_method_header = {
description = "header used to forward the original request method",
type = "string",
default = "X-Original-Method",
minLength = 1,
maxLength = 128,
},
query = {
type = "object",
properties = {
upstream_method = {
type = "string",
enum = {"post", "query"},
default = "post",
},
},
additionalProperties = false,
},
post = {
type = "object",
properties = {
cache_enabled = {type = "boolean", default = false},
read_only = {type = "boolean", default = false},
},
additionalProperties = false,
},
cache = cache_schema,
},
additionalProperties = false,
encrypt_fields = {"cache.redis_password"},
}

local _M = {
version = 0.3,
priority = -1001,
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

if conf.preserve_original_method_header ~= false
and not core.utils.validate_header_field(conf.original_method_header
or "X-Original-Method") then
return false, "invalid original_method_header"
end

local cache_conf = conf.cache
if cache_conf and cache_conf.enabled then
if cache_conf.backend == "redis" and not cache_conf.redis_host then
return false, "cache.redis_host is required for the redis backend"
end

if cache_conf.backend == "redis-cluster"
and (not cache_conf.redis_cluster_name or not cache_conf.redis_cluster_nodes) then
return false, "cache.redis_cluster_name and cache.redis_cluster_nodes are required " ..
"for the redis-cluster backend"
end
end

return true
end

function _M.access(conf, ctx)
local method = ngx.req.get_method()
if method ~= "QUERY" and method ~= "POST" then
return
end

ctx.query_gateway_client_method = method

local cache_conf = conf.cache
local post_cache_enabled = conf.post and conf.post.cache_enabled and conf.post.read_only
if cache_conf and cache_conf.enabled and (method == "QUERY" or post_cache_enabled) then
local entry, status = cache.fetch(cache_conf, ctx)
if entry then
ctx.query_gateway_cache_hit = true
return cache.serve(entry)
end

if method == "QUERY" and status == "missing content-type" then
return 400
end
end

if method ~= "QUERY" then
return
end

ctx.query_gateway_original_method = method

if conf.preserve_original_method_header ~= false then
core.request.set_header(ctx, conf.original_method_header or "X-Original-Method", method)
end

if not conf.query or conf.query.upstream_method ~= "query" then
ngx.req.set_method(ngx.HTTP_POST)
end
end
function _M.header_filter(conf, ctx)
if conf.cache and conf.cache.enabled then
cache.header_filter(conf.cache, ctx)
end
end

function _M.body_filter(conf, ctx)
if conf.cache and conf.cache.enabled then
cache.body_filter(conf.cache, ctx)
end
end

return _M
Loading
Loading