decidim-api 0.31.0 → 0.31.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/decidim/api/queries_controller.rb +2 -1
- data/config/locales/am-ET.yml +1 -0
- data/config/locales/ar.yml +1 -0
- data/config/locales/bg.yml +1 -0
- data/config/locales/bn-BD.yml +1 -0
- data/config/locales/bs-BA.yml +1 -0
- data/config/locales/ca-IT.yml +8 -0
- data/config/locales/ca.yml +8 -0
- data/config/locales/cs.yml +1 -0
- data/config/locales/da.yml +1 -0
- data/config/locales/de.yml +1 -0
- data/config/locales/el.yml +1 -0
- data/config/locales/en.yml +8 -0
- data/config/locales/eo.yml +1 -0
- data/config/locales/es-MX.yml +8 -0
- data/config/locales/es-PY.yml +8 -0
- data/config/locales/es.yml +8 -0
- data/config/locales/et.yml +1 -0
- data/config/locales/eu.yml +6 -0
- data/config/locales/fa-IR.yml +1 -0
- data/config/locales/fi-plain.yml +8 -0
- data/config/locales/fi.yml +8 -0
- data/config/locales/fr-CA.yml +8 -0
- data/config/locales/fr.yml +8 -0
- data/config/locales/ga-IE.yml +1 -0
- data/config/locales/gl.yml +1 -0
- data/config/locales/gn-PY.yml +1 -0
- data/config/locales/he-IL.yml +1 -0
- data/config/locales/hr.yml +1 -0
- data/config/locales/hu.yml +1 -0
- data/config/locales/id-ID.yml +1 -0
- data/config/locales/is-IS.yml +1 -0
- data/config/locales/it.yml +1 -0
- data/config/locales/ja.yml +8 -0
- data/config/locales/ka-GE.yml +1 -0
- data/config/locales/kaa.yml +1 -0
- data/config/locales/ko.yml +1 -0
- data/config/locales/lb.yml +1 -0
- data/config/locales/lo-LA.yml +1 -0
- data/config/locales/lt.yml +1 -0
- data/config/locales/lv.yml +1 -0
- data/config/locales/mt.yml +1 -0
- data/config/locales/nl.yml +1 -0
- data/config/locales/no.yml +6 -0
- data/config/locales/oc-FR.yml +1 -0
- data/config/locales/om-ET.yml +1 -0
- data/config/locales/pl.yml +1 -0
- data/config/locales/pt-BR.yml +8 -0
- data/config/locales/pt.yml +1 -0
- data/config/locales/ro-RO.yml +8 -0
- data/config/locales/ru.yml +1 -0
- data/config/locales/si-LK.yml +1 -0
- data/config/locales/sk.yml +1 -0
- data/config/locales/sl.yml +1 -0
- data/config/locales/so-SO.yml +1 -0
- data/config/locales/sq-AL.yml +1 -0
- data/config/locales/sr-CS.yml +1 -0
- data/config/locales/sv.yml +1 -0
- data/config/locales/sw-KE.yml +1 -0
- data/config/locales/th-TH.yml +1 -0
- data/config/locales/ti-ER.yml +1 -0
- data/config/locales/tr-TR.yml +1 -0
- data/config/locales/uk.yml +1 -0
- data/config/locales/val-ES.yml +1 -0
- data/config/locales/vi.yml +1 -0
- data/config/locales/zh-CN.yml +1 -0
- data/config/locales/zh-TW.yml +1 -0
- data/lib/decidim/api/alias_analyzer.rb +23 -0
- data/lib/decidim/api/errors/introspection_disabled_error.rb +14 -0
- data/lib/decidim/api/errors/recursion_limit_exceeded_error.rb +14 -0
- data/lib/decidim/api/errors/too_many_aliases_error.rb +15 -0
- data/lib/decidim/api/introspection_analyzer.rb +51 -0
- data/lib/decidim/api/recursion_analyzer.rb +74 -0
- data/lib/decidim/api/schema.rb +4 -0
- data/lib/decidim/api/test/component_context.rb +1 -0
- data/lib/decidim/api/test/type_context.rb +94 -2
- data/lib/decidim/api/types.rb +9 -0
- data/lib/decidim/api/version.rb +1 -1
- data/lib/decidim/api.rb +14 -2
- metadata +84 -12
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a229eb60485e45b884a7db5f974fa518fee469dd3e83baf02d0d9cdb27e2e808
|
|
4
|
+
data.tar.gz: 6834b6467deb4c9325aeab500d77fa41ecab50f8f2d3ddb6b74de963fdbecf96
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d52282c385a287f6484916c9077b687550a17d67eb47dfa4cecd6920119d5195d629439c270fb24edca6c52a0333170899016b5e07c347b3ebf029061ce5fef4
|
|
7
|
+
data.tar.gz: 1293a67073053c56ffdb6c25cd6bdcf8d3168216ae484ada6b11648aa170a1a461cda61dfe5f3b95ed9a6dde5a7a1d62d444e510a4b7d8e5bf29fd4a6e3cf2e9
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
am:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ar:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
bg:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
bn:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
bs:
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
ca-IT:
|
|
3
|
+
decidim:
|
|
4
|
+
api:
|
|
5
|
+
errors:
|
|
6
|
+
introspection_disabled: La introspecció està desactivada per a aquesta sol·licitud
|
|
7
|
+
recursion_limit_exceeded_error: S'han detectat massa consultes recurrents
|
|
8
|
+
too_many_aliases_error: S'han fet servir massa àlies (nicknames). Has fet servir %{size} àlies, però es permet un màxim de %{limit}.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
ca:
|
|
3
|
+
decidim:
|
|
4
|
+
api:
|
|
5
|
+
errors:
|
|
6
|
+
introspection_disabled: La introspecció està desactivada per a aquesta sol·licitud
|
|
7
|
+
recursion_limit_exceeded_error: S'han detectat massa consultes recurrents
|
|
8
|
+
too_many_aliases_error: S'han fet servir massa àlies (nicknames). Has fet servir %{size} àlies, però es permet un màxim de %{limit}.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cs:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
da:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
de:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
el:
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
en:
|
|
3
|
+
decidim:
|
|
4
|
+
api:
|
|
5
|
+
errors:
|
|
6
|
+
introspection_disabled: Introspection is disabled for this request
|
|
7
|
+
recursion_limit_exceeded_error: Too many recursions detected in query
|
|
8
|
+
too_many_aliases_error: Too many aliases used. You have used %{size} aliases, but %{limit} are allowed.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
eo:
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
es-MX:
|
|
3
|
+
decidim:
|
|
4
|
+
api:
|
|
5
|
+
errors:
|
|
6
|
+
introspection_disabled: La introspección está desactivada para esta solicitud
|
|
7
|
+
recursion_limit_exceeded_error: Se han detectado demasiadas consultas recurrentes
|
|
8
|
+
too_many_aliases_error: Has utilizado demasiados alias (nicknames). Has usado %{size} alias, pero se permite un máximo de %{limit}.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
es-PY:
|
|
3
|
+
decidim:
|
|
4
|
+
api:
|
|
5
|
+
errors:
|
|
6
|
+
introspection_disabled: La introspección está desactivada para esta solicitud
|
|
7
|
+
recursion_limit_exceeded_error: Se han detectado demasiadas consultas recurrentes
|
|
8
|
+
too_many_aliases_error: Has utilizado demasiados alias (nicknames). Has usado %{size} alias, pero se permite un máximo de %{limit}.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
es:
|
|
3
|
+
decidim:
|
|
4
|
+
api:
|
|
5
|
+
errors:
|
|
6
|
+
introspection_disabled: La introspección está desactivada para esta solicitud
|
|
7
|
+
recursion_limit_exceeded_error: Se han detectado demasiadas consultas recurrentes
|
|
8
|
+
too_many_aliases_error: Has utilizado demasiados alias (nicknames). Has usado %{size} alias, pero se permite un máximo de %{limit}.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
et:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
fa:
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
fi-pl:
|
|
3
|
+
decidim:
|
|
4
|
+
api:
|
|
5
|
+
errors:
|
|
6
|
+
introspection_disabled: Rajapintamallin tarkastelu ei ole käytössä tälle pyynnölle
|
|
7
|
+
recursion_limit_exceeded_error: Rajapintakyselyssä on liikaa rekursiivisia kyselyitä
|
|
8
|
+
too_many_aliases_error: Rajapintakysely käyttää liian monta alias-nimitystä. Kyselyssä on %{size} aliasta, mutta maksimissaan %{limit} on sallittu.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
fi:
|
|
3
|
+
decidim:
|
|
4
|
+
api:
|
|
5
|
+
errors:
|
|
6
|
+
introspection_disabled: Rajapintamallin tarkastelu ei ole käytössä tälle pyynnölle
|
|
7
|
+
recursion_limit_exceeded_error: Rajapintakyselyssä on liikaa rekursiivisia kyselyitä
|
|
8
|
+
too_many_aliases_error: Rajapintakysely käyttää liian monta alias-nimitystä. Kyselyssä on %{size} aliasta, mutta maksimissaan %{limit} on sallittu.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
fr-CA:
|
|
3
|
+
decidim:
|
|
4
|
+
api:
|
|
5
|
+
errors:
|
|
6
|
+
introspection_disabled: L'introspection est désactivée pour cette requête
|
|
7
|
+
recursion_limit_exceeded_error: Trop de récursions détectées dans la requête
|
|
8
|
+
too_many_aliases_error: Trop d'alias utilisés. Vous avez utilisé %{size} alias, mais seulement %{limit} sont autorisés.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
fr:
|
|
3
|
+
decidim:
|
|
4
|
+
api:
|
|
5
|
+
errors:
|
|
6
|
+
introspection_disabled: L'introspection est désactivée pour cette requête
|
|
7
|
+
recursion_limit_exceeded_error: Trop de récursions détectées dans la requête
|
|
8
|
+
too_many_aliases_error: Trop d'alias utilisés. Vous avez utilisé %{size} alias, mais seulement %{limit} sont autorisés.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ga:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
gl:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
gn:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
he:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
hr:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
hu:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
id:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
is-IS:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
it:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ka:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
kaa:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ko:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
lb:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
lo:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
lt:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
lv:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
mt:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
nl:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
oc:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
om:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pl:
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
pt-BR:
|
|
3
|
+
decidim:
|
|
4
|
+
api:
|
|
5
|
+
errors:
|
|
6
|
+
introspection_disabled: A inspeção está desabilitada para esta requisição
|
|
7
|
+
recursion_limit_exceeded_error: Muitas recursões detectadas na consulta
|
|
8
|
+
too_many_aliases_error: Foram utilizados muitos aliases. Você usou %{size} aliases, mas são permitidos apenas %{limit}.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pt:
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
ro:
|
|
3
|
+
decidim:
|
|
4
|
+
api:
|
|
5
|
+
errors:
|
|
6
|
+
introspection_disabled: Introspection este dezactivat pentru această solicitare
|
|
7
|
+
recursion_limit_exceeded_error: Prea multe recursii detectate în interogare
|
|
8
|
+
too_many_aliases_error: Prea multe aliasuri folosite. Ați folosit aliasuri %{size}, dar %{limit} sunt permise.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ru:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
si:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sk:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sl:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
so:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sq:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sr:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sv:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sw:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
th:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ti:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
tr:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
uk:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
val:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
vi:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
zh-CN:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
zh-TW:
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Api
|
|
5
|
+
class AliasAnalyzer < GraphQL::Analysis::AST::Analyzer
|
|
6
|
+
def initialize(query)
|
|
7
|
+
super
|
|
8
|
+
|
|
9
|
+
@aliases = Set.new
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def on_enter_field(node, _parent, _visitor)
|
|
13
|
+
@aliases.add(node.alias) if node.alias.present?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def result
|
|
17
|
+
if @aliases.size > Decidim::Api.max_aliases
|
|
18
|
+
Errors::TooManyAliasesError.new(I18n.t("decidim.api.errors.too_many_aliases_error", size: @aliases.size, limit: Decidim::Api.max_aliases))
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Api
|
|
5
|
+
module Errors
|
|
6
|
+
# i18n-tasks-use t("decidim.api.errors.introspection_disabled")
|
|
7
|
+
class IntrospectionDisabledError < GraphQL::ExecutionError
|
|
8
|
+
def to_h
|
|
9
|
+
super.merge({ "extensions" => { "code" => "INTROSPECTION_DISABLED_ERROR" } })
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Api
|
|
5
|
+
module Errors
|
|
6
|
+
# i18n-tasks-use t("decidim.api.errors.recursion_limit_exceeded_error")
|
|
7
|
+
class RecursionLimitExceededError < GraphQL::AnalysisError
|
|
8
|
+
def to_h
|
|
9
|
+
super.merge({ "extensions" => { "code" => "RECURSION_LIMIT_EXCEEDED_ERROR" } })
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Api
|
|
5
|
+
module Errors
|
|
6
|
+
# i18n-tasks-use t("decidim.api.errors.too_many_aliases_error")
|
|
7
|
+
|
|
8
|
+
class TooManyAliasesError < GraphQL::AnalysisError
|
|
9
|
+
def to_h
|
|
10
|
+
super.merge({ "extensions" => { "code" => "TOO_MANY_ALIASES_ERROR" } })
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Api
|
|
5
|
+
module IntrospectionAnalyzer
|
|
6
|
+
module FieldVisibility
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
included do
|
|
10
|
+
def self.visible?(context)
|
|
11
|
+
raise Errors::IntrospectionDisabledError, I18n.t("decidim.api.errors.introspection_disabled") unless context[:can_introspect] == true
|
|
12
|
+
|
|
13
|
+
super
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class SchemaType < GraphQL::Introspection::SchemaType
|
|
19
|
+
include FieldVisibility
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class TypeType < GraphQL::Introspection::TypeType
|
|
23
|
+
include FieldVisibility
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class DirectiveType < GraphQL::Introspection::DirectiveType
|
|
27
|
+
include FieldVisibility
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class DirectiveLocationEnum < GraphQL::Introspection::DirectiveLocationEnum
|
|
31
|
+
include FieldVisibility
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class EnumValueType < GraphQL::Introspection::EnumValueType
|
|
35
|
+
include FieldVisibility
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class FieldType < GraphQL::Introspection::FieldType
|
|
39
|
+
include FieldVisibility
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class InputValueType < GraphQL::Introspection::InputValueType
|
|
43
|
+
include FieldVisibility
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class TypeKindEnum < GraphQL::Introspection::TypeKindEnum
|
|
47
|
+
include FieldVisibility
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This analyzer checks for too many recursions in GraphQL queries.
|
|
4
|
+
# Copyright (c) GitLab B.V.
|
|
5
|
+
# License: MIT Expat license
|
|
6
|
+
# This content of the class was copied from the GitLab repository
|
|
7
|
+
# @see https://gitlab.com/gitlab-org/gitlab/-/blob/f59f7aa0d86f07496e68abf7172edd703669e7bd/lib/gitlab/graphql/query_analyzers/ast/recursion_analyzer.rb
|
|
8
|
+
# To which I have modified the result format to be compatible with decidim-api.
|
|
9
|
+
|
|
10
|
+
module Decidim
|
|
11
|
+
module Api
|
|
12
|
+
class RecursionAnalyzer < GraphQL::Analysis::AST::Analyzer
|
|
13
|
+
IGNORED_FIELDS = %w(node edges nodes ofType).freeze
|
|
14
|
+
RECURSION_THRESHOLD = 2
|
|
15
|
+
|
|
16
|
+
def initialize(query)
|
|
17
|
+
super
|
|
18
|
+
|
|
19
|
+
@node_visits = {}
|
|
20
|
+
@recurring_fields = {}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def on_enter_field(node, _parent, visitor)
|
|
24
|
+
return if skip_node?(node, visitor)
|
|
25
|
+
|
|
26
|
+
node_name = node.name
|
|
27
|
+
node_visits[node_name] ||= 0
|
|
28
|
+
node_visits[node_name] += 1
|
|
29
|
+
|
|
30
|
+
times_encountered = @node_visits[node_name]
|
|
31
|
+
recurring_fields[node_name] = times_encountered if recursion_too_deep?(node_name, times_encountered)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Visitors are all defined on the AST::Analyzer base class
|
|
35
|
+
# We override them for custom analyzers.
|
|
36
|
+
def on_leave_field(node, _parent, visitor)
|
|
37
|
+
return if skip_node?(node, visitor)
|
|
38
|
+
|
|
39
|
+
node_name = node.name
|
|
40
|
+
node_visits[node_name] ||= 0
|
|
41
|
+
node_visits[node_name] -= 1
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def result
|
|
45
|
+
@recurring_fields = @recurring_fields.select { |k, v| recursion_too_deep?(k, v) }
|
|
46
|
+
|
|
47
|
+
Decidim::Api::Errors::RecursionLimitExceededError.new I18n.t("decidim.api.errors.recursion_limit_exceeded_error") if @recurring_fields.any?
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
attr_reader :node_visits, :recurring_fields
|
|
53
|
+
|
|
54
|
+
def recursion_too_deep?(node_name, times_encountered)
|
|
55
|
+
return false if IGNORED_FIELDS.include?(node_name)
|
|
56
|
+
|
|
57
|
+
times_encountered > recursion_threshold
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def skip_node?(node, visitor)
|
|
61
|
+
# We do not want to count skipped fields or fields
|
|
62
|
+
# inside fragment definitions
|
|
63
|
+
return false if visitor.skipping? || visitor.visiting_fragment_definition?
|
|
64
|
+
|
|
65
|
+
!node.is_a?(GraphQL::Language::Nodes::Field) || node.selections.empty?
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Separated into a method to allow overriding or customization of the recursion limit.
|
|
69
|
+
def recursion_threshold
|
|
70
|
+
RECURSION_THRESHOLD
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
data/lib/decidim/api/schema.rb
CHANGED
|
@@ -7,6 +7,10 @@ module Decidim
|
|
|
7
7
|
mutation(MutationType)
|
|
8
8
|
query(QueryType)
|
|
9
9
|
|
|
10
|
+
introspection IntrospectionAnalyzer
|
|
11
|
+
query_analyzer RecursionAnalyzer
|
|
12
|
+
query_analyzer AliasAnalyzer
|
|
13
|
+
|
|
10
14
|
default_max_page_size Decidim::Api.schema_max_per_page
|
|
11
15
|
max_depth Decidim::Api.schema_max_depth
|
|
12
16
|
max_complexity Decidim::Api.schema_max_complexity
|
|
@@ -15,6 +15,7 @@ shared_context "with a graphql class type" do
|
|
|
15
15
|
let(:type_class) { described_class }
|
|
16
16
|
let(:variables) { {} }
|
|
17
17
|
let(:root_value) { model }
|
|
18
|
+
let(:can_introspect) { Decidim::Api.enable_anonymous_introspection || current_user&.admin? }
|
|
18
19
|
|
|
19
20
|
let(:schema) do
|
|
20
21
|
klass = type_class
|
|
@@ -28,6 +29,20 @@ shared_context "with a graphql class type" do
|
|
|
28
29
|
execute_query query, variables.stringify_keys
|
|
29
30
|
end
|
|
30
31
|
|
|
32
|
+
def raise_proper_error(error)
|
|
33
|
+
code = error.dig("extensions", "code")
|
|
34
|
+
|
|
35
|
+
# Matches the error code with the Error class
|
|
36
|
+
# For instance, if the error code is NOT_FOUND_ERROR then it will raise the "Decidim::Api::Errors::NotFoundError" class
|
|
37
|
+
raise "Decidim::Api::Errors::#{code.downcase.classify}".constantize, error["message"] if %w(
|
|
38
|
+
INTROSPECTION_DISABLED_ERROR
|
|
39
|
+
TOO_MANY_ALIASES_ERROR
|
|
40
|
+
RECURSION_LIMIT_EXCEEDED_ERROR
|
|
41
|
+
).include?(code)
|
|
42
|
+
|
|
43
|
+
raise GraphQL::ExecutionError, error["message"]
|
|
44
|
+
end
|
|
45
|
+
|
|
31
46
|
def execute_query(query, variables)
|
|
32
47
|
result = schema.execute(
|
|
33
48
|
query,
|
|
@@ -36,17 +51,94 @@ shared_context "with a graphql class type" do
|
|
|
36
51
|
current_organization:,
|
|
37
52
|
current_user:,
|
|
38
53
|
current_component:,
|
|
39
|
-
scopes: api_scopes
|
|
54
|
+
scopes: api_scopes,
|
|
55
|
+
can_introspect:
|
|
40
56
|
},
|
|
41
57
|
variables:
|
|
42
58
|
)
|
|
43
59
|
|
|
44
|
-
|
|
60
|
+
raise_proper_error(result["errors"].first) if result["errors"]
|
|
45
61
|
|
|
46
62
|
result["data"]
|
|
47
63
|
end
|
|
48
64
|
end
|
|
49
65
|
|
|
66
|
+
shared_examples "when the introspection is disabled" do
|
|
67
|
+
shared_examples "check introspection behavior" do
|
|
68
|
+
context "and the user is not authenticated" do
|
|
69
|
+
let!(:current_user) { nil }
|
|
70
|
+
|
|
71
|
+
it "raises an Decidim::Api::Errors::IntrospectionDisabledError" do
|
|
72
|
+
expect { response }.to raise_error(Decidim::Api::Errors::IntrospectionDisabledError, "Introspection is disabled for this request")
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
context "and the user is not an admin" do
|
|
77
|
+
let!(:current_user) { create(:user, :confirmed, organization: current_organization) }
|
|
78
|
+
|
|
79
|
+
it "raises an Decidim::Api::Errors::IntrospectionDisabledError" do
|
|
80
|
+
expect { response }.to raise_error(Decidim::Api::Errors::IntrospectionDisabledError, "Introspection is disabled for this request")
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
context "and the user is an admin" do
|
|
85
|
+
let!(:current_user) { create(:user, :confirmed, :admin, organization: current_organization) }
|
|
86
|
+
|
|
87
|
+
it "runs successfully" do
|
|
88
|
+
expect { response }.not_to raise_error
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
context "and the setting is true" do
|
|
93
|
+
before do
|
|
94
|
+
allow(Decidim::Api).to receive(:enable_anonymous_introspection).and_return(true)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "runs successfully" do
|
|
98
|
+
expect { response }.not_to raise_error
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
context "and the setting is false" do
|
|
103
|
+
before do
|
|
104
|
+
allow(Decidim::Api).to receive(:enable_anonymous_introspection).and_return(false)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "raises an Decidim::Api::Errors::IntrospectionDisabledError" do
|
|
108
|
+
expect { response }.to raise_error(Decidim::Api::Errors::IntrospectionDisabledError, "Introspection is disabled for this request")
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
context "when requesting the schema introspection" do
|
|
114
|
+
let(:query) do
|
|
115
|
+
%( query { __schema { types { fields { type { fields { type { name } } } } } } } )
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it_behaves_like "check introspection behavior"
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
context "when requesting the type introspection" do
|
|
122
|
+
let(:query) do
|
|
123
|
+
%( query CircularIntrospection {
|
|
124
|
+
__type(name: "User") {
|
|
125
|
+
fields {
|
|
126
|
+
type {
|
|
127
|
+
fields {
|
|
128
|
+
type {
|
|
129
|
+
name
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
} )
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it_behaves_like "check introspection behavior"
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
50
142
|
shared_context "with a graphql scalar class type" do
|
|
51
143
|
include_context "with a graphql class type"
|
|
52
144
|
|
data/lib/decidim/api/types.rb
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
module Decidim
|
|
4
4
|
module Api
|
|
5
|
+
autoload :RecursionAnalyzer, "decidim/api/recursion_analyzer"
|
|
6
|
+
autoload :IntrospectionAnalyzer, "decidim/api/introspection_analyzer"
|
|
7
|
+
autoload :AliasAnalyzer, "decidim/api/alias_analyzer"
|
|
5
8
|
autoload :QueryType, "decidim/api/query_type"
|
|
6
9
|
autoload :MutationType, "decidim/api/mutation_type"
|
|
7
10
|
autoload :Schema, "decidim/api/schema"
|
|
@@ -9,6 +12,12 @@ module Decidim
|
|
|
9
12
|
autoload :GraphqlPermissions, "decidim/api/graphql_permissions"
|
|
10
13
|
autoload :ComponentMutationType, "decidim/api/component_mutation_type"
|
|
11
14
|
|
|
15
|
+
module Errors
|
|
16
|
+
autoload :IntrospectionDisabledError, "decidim/api/errors/introspection_disabled_error"
|
|
17
|
+
autoload :TooManyAliasesError, "decidim/api/errors/too_many_aliases_error"
|
|
18
|
+
autoload :RecursionLimitExceededError, "decidim/api/errors/recursion_limit_exceeded_error"
|
|
19
|
+
end
|
|
20
|
+
|
|
12
21
|
module Types
|
|
13
22
|
autoload :BaseArgument, "decidim/api/types/base_argument"
|
|
14
23
|
autoload :BaseEnum, "decidim/api/types/base_enum"
|
data/lib/decidim/api/version.rb
CHANGED
data/lib/decidim/api.rb
CHANGED
|
@@ -23,6 +23,11 @@ module Decidim
|
|
|
23
23
|
Decidim::Env.new("API_SCHEMA_MAX_COMPLEXITY", 5000).to_i
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
# defines how many aliases are permitted in a query
|
|
27
|
+
config_accessor :max_aliases do
|
|
28
|
+
Decidim::Env.new("API_SCHEMA_MAX_ALIASES", 5).to_i
|
|
29
|
+
end
|
|
30
|
+
|
|
26
31
|
# defines the schema max_depth to configure GraphQL query max_depth
|
|
27
32
|
config_accessor :schema_max_depth do
|
|
28
33
|
Decidim::Env.new("API_SCHEMA_MAX_DEPTH", 15).to_i
|
|
@@ -32,12 +37,19 @@ module Decidim
|
|
|
32
37
|
Decidim::Env.new("DECIDIM_API_DISCLOSE_SYSTEM_VERSION").present?
|
|
33
38
|
end
|
|
34
39
|
|
|
35
|
-
#
|
|
36
|
-
|
|
40
|
+
# makes the API authentication necessary in order to access it
|
|
41
|
+
|
|
37
42
|
config_accessor :force_api_authentication do
|
|
38
43
|
Decidim::Env.new("DECIDIM_API_FORCE_API_AUTHENTICATION", nil).present?
|
|
39
44
|
end
|
|
40
45
|
|
|
46
|
+
# Allows anonymous introspection queries.
|
|
47
|
+
# If you are not sure, leave it set to false. In this way, only administrator users will be able to access the introspection query.
|
|
48
|
+
# Otherwise, anyone can access it, which may cause security issues.
|
|
49
|
+
config_accessor :enable_anonymous_introspection do
|
|
50
|
+
Decidim::Env.new("DECIDIM_API_ENABLE_ANONYMOUS_INTROSPECTION", nil).present?
|
|
51
|
+
end
|
|
52
|
+
|
|
41
53
|
# The expiration time of the JWT tokens, after which issued token will
|
|
42
54
|
# expire. Recommended to match the value of
|
|
43
55
|
# `DECIDIM_OAUTH_ACCESS_TOKEN_EXPIRES_IN`.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: decidim-api
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.31.
|
|
4
|
+
version: 0.31.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Josep Jaume Rey Peroy
|
|
@@ -10,7 +10,7 @@ authors:
|
|
|
10
10
|
autorequire:
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date:
|
|
13
|
+
date: 2026-01-28 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: decidim-core
|
|
@@ -18,14 +18,14 @@ dependencies:
|
|
|
18
18
|
requirements:
|
|
19
19
|
- - '='
|
|
20
20
|
- !ruby/object:Gem::Version
|
|
21
|
-
version: 0.31.
|
|
21
|
+
version: 0.31.1
|
|
22
22
|
type: :runtime
|
|
23
23
|
prerelease: false
|
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
25
25
|
requirements:
|
|
26
26
|
- - '='
|
|
27
27
|
- !ruby/object:Gem::Version
|
|
28
|
-
version: 0.31.
|
|
28
|
+
version: 0.31.1
|
|
29
29
|
- !ruby/object:Gem::Dependency
|
|
30
30
|
name: devise-jwt
|
|
31
31
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -94,56 +94,56 @@ dependencies:
|
|
|
94
94
|
requirements:
|
|
95
95
|
- - '='
|
|
96
96
|
- !ruby/object:Gem::Version
|
|
97
|
-
version: 0.31.
|
|
97
|
+
version: 0.31.1
|
|
98
98
|
type: :development
|
|
99
99
|
prerelease: false
|
|
100
100
|
version_requirements: !ruby/object:Gem::Requirement
|
|
101
101
|
requirements:
|
|
102
102
|
- - '='
|
|
103
103
|
- !ruby/object:Gem::Version
|
|
104
|
-
version: 0.31.
|
|
104
|
+
version: 0.31.1
|
|
105
105
|
- !ruby/object:Gem::Dependency
|
|
106
106
|
name: decidim-comments
|
|
107
107
|
requirement: !ruby/object:Gem::Requirement
|
|
108
108
|
requirements:
|
|
109
109
|
- - '='
|
|
110
110
|
- !ruby/object:Gem::Version
|
|
111
|
-
version: 0.31.
|
|
111
|
+
version: 0.31.1
|
|
112
112
|
type: :development
|
|
113
113
|
prerelease: false
|
|
114
114
|
version_requirements: !ruby/object:Gem::Requirement
|
|
115
115
|
requirements:
|
|
116
116
|
- - '='
|
|
117
117
|
- !ruby/object:Gem::Version
|
|
118
|
-
version: 0.31.
|
|
118
|
+
version: 0.31.1
|
|
119
119
|
- !ruby/object:Gem::Dependency
|
|
120
120
|
name: decidim-dev
|
|
121
121
|
requirement: !ruby/object:Gem::Requirement
|
|
122
122
|
requirements:
|
|
123
123
|
- - '='
|
|
124
124
|
- !ruby/object:Gem::Version
|
|
125
|
-
version: 0.31.
|
|
125
|
+
version: 0.31.1
|
|
126
126
|
type: :development
|
|
127
127
|
prerelease: false
|
|
128
128
|
version_requirements: !ruby/object:Gem::Requirement
|
|
129
129
|
requirements:
|
|
130
130
|
- - '='
|
|
131
131
|
- !ruby/object:Gem::Version
|
|
132
|
-
version: 0.31.
|
|
132
|
+
version: 0.31.1
|
|
133
133
|
- !ruby/object:Gem::Dependency
|
|
134
134
|
name: decidim-participatory_processes
|
|
135
135
|
requirement: !ruby/object:Gem::Requirement
|
|
136
136
|
requirements:
|
|
137
137
|
- - '='
|
|
138
138
|
- !ruby/object:Gem::Version
|
|
139
|
-
version: 0.31.
|
|
139
|
+
version: 0.31.1
|
|
140
140
|
type: :development
|
|
141
141
|
prerelease: false
|
|
142
142
|
version_requirements: !ruby/object:Gem::Requirement
|
|
143
143
|
requirements:
|
|
144
144
|
- - '='
|
|
145
145
|
- !ruby/object:Gem::Version
|
|
146
|
-
version: 0.31.
|
|
146
|
+
version: 0.31.1
|
|
147
147
|
description: API engine for decidim
|
|
148
148
|
email:
|
|
149
149
|
- josepjaume@gmail.com
|
|
@@ -173,18 +173,90 @@ files:
|
|
|
173
173
|
- app/views/layouts/decidim/api/documentation.html.erb
|
|
174
174
|
- config/assets.rb
|
|
175
175
|
- config/initializers/devise.rb
|
|
176
|
+
- config/locales/am-ET.yml
|
|
177
|
+
- config/locales/ar.yml
|
|
178
|
+
- config/locales/bg.yml
|
|
179
|
+
- config/locales/bn-BD.yml
|
|
180
|
+
- config/locales/bs-BA.yml
|
|
181
|
+
- config/locales/ca-IT.yml
|
|
182
|
+
- config/locales/ca.yml
|
|
183
|
+
- config/locales/cs.yml
|
|
184
|
+
- config/locales/da.yml
|
|
185
|
+
- config/locales/de.yml
|
|
186
|
+
- config/locales/el.yml
|
|
187
|
+
- config/locales/en.yml
|
|
188
|
+
- config/locales/eo.yml
|
|
189
|
+
- config/locales/es-MX.yml
|
|
190
|
+
- config/locales/es-PY.yml
|
|
191
|
+
- config/locales/es.yml
|
|
192
|
+
- config/locales/et.yml
|
|
193
|
+
- config/locales/eu.yml
|
|
194
|
+
- config/locales/fa-IR.yml
|
|
195
|
+
- config/locales/fi-plain.yml
|
|
196
|
+
- config/locales/fi.yml
|
|
197
|
+
- config/locales/fr-CA.yml
|
|
198
|
+
- config/locales/fr.yml
|
|
199
|
+
- config/locales/ga-IE.yml
|
|
200
|
+
- config/locales/gl.yml
|
|
201
|
+
- config/locales/gn-PY.yml
|
|
202
|
+
- config/locales/he-IL.yml
|
|
203
|
+
- config/locales/hr.yml
|
|
204
|
+
- config/locales/hu.yml
|
|
205
|
+
- config/locales/id-ID.yml
|
|
206
|
+
- config/locales/is-IS.yml
|
|
207
|
+
- config/locales/it.yml
|
|
208
|
+
- config/locales/ja.yml
|
|
209
|
+
- config/locales/ka-GE.yml
|
|
210
|
+
- config/locales/kaa.yml
|
|
211
|
+
- config/locales/ko.yml
|
|
212
|
+
- config/locales/lb.yml
|
|
213
|
+
- config/locales/lo-LA.yml
|
|
214
|
+
- config/locales/lt.yml
|
|
215
|
+
- config/locales/lv.yml
|
|
216
|
+
- config/locales/mt.yml
|
|
217
|
+
- config/locales/nl.yml
|
|
218
|
+
- config/locales/no.yml
|
|
219
|
+
- config/locales/oc-FR.yml
|
|
220
|
+
- config/locales/om-ET.yml
|
|
221
|
+
- config/locales/pl.yml
|
|
222
|
+
- config/locales/pt-BR.yml
|
|
223
|
+
- config/locales/pt.yml
|
|
224
|
+
- config/locales/ro-RO.yml
|
|
225
|
+
- config/locales/ru.yml
|
|
226
|
+
- config/locales/si-LK.yml
|
|
227
|
+
- config/locales/sk.yml
|
|
228
|
+
- config/locales/sl.yml
|
|
229
|
+
- config/locales/so-SO.yml
|
|
230
|
+
- config/locales/sq-AL.yml
|
|
231
|
+
- config/locales/sr-CS.yml
|
|
232
|
+
- config/locales/sv.yml
|
|
233
|
+
- config/locales/sw-KE.yml
|
|
234
|
+
- config/locales/th-TH.yml
|
|
235
|
+
- config/locales/ti-ER.yml
|
|
236
|
+
- config/locales/tr-TR.yml
|
|
237
|
+
- config/locales/uk.yml
|
|
238
|
+
- config/locales/val-ES.yml
|
|
239
|
+
- config/locales/vi.yml
|
|
240
|
+
- config/locales/zh-CN.yml
|
|
241
|
+
- config/locales/zh-TW.yml
|
|
176
242
|
- config/routes.rb
|
|
177
243
|
- decidim-api.gemspec
|
|
178
244
|
- docs/usage.md
|
|
179
245
|
- lib/decidim/api.rb
|
|
246
|
+
- lib/decidim/api/alias_analyzer.rb
|
|
180
247
|
- lib/decidim/api/component_mutation_type.rb
|
|
181
248
|
- lib/decidim/api/devise.rb
|
|
182
249
|
- lib/decidim/api/engine.rb
|
|
250
|
+
- lib/decidim/api/errors/introspection_disabled_error.rb
|
|
251
|
+
- lib/decidim/api/errors/recursion_limit_exceeded_error.rb
|
|
252
|
+
- lib/decidim/api/errors/too_many_aliases_error.rb
|
|
183
253
|
- lib/decidim/api/graphiql-initial-query.txt
|
|
184
254
|
- lib/decidim/api/graphiql/config.rb
|
|
185
255
|
- lib/decidim/api/graphql_permissions.rb
|
|
256
|
+
- lib/decidim/api/introspection_analyzer.rb
|
|
186
257
|
- lib/decidim/api/mutation_type.rb
|
|
187
258
|
- lib/decidim/api/query_type.rb
|
|
259
|
+
- lib/decidim/api/recursion_analyzer.rb
|
|
188
260
|
- lib/decidim/api/required_scopes.rb
|
|
189
261
|
- lib/decidim/api/schema.rb
|
|
190
262
|
- lib/decidim/api/test.rb
|