decidim-api 0.30.3 → 0.30.5
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/decidim_introspection.rb +51 -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/recursion_analyzer.rb +74 -0
- data/lib/decidim/api/schema.rb +6 -0
- data/lib/decidim/api/test/component_context.rb +1 -0
- data/lib/decidim/api/test/type_context.rb +93 -2
- data/lib/decidim/api/test.rb +1 -0
- data/lib/decidim/api/types.rb +9 -0
- data/lib/decidim/api/version.rb +1 -1
- data/lib/decidim/api.rb +12 -0
- 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: 7ca33c846496c357db67439e87dbdb4ad6639c13fd2a823a52c6f270be3d5d0e
|
|
4
|
+
data.tar.gz: 346529a751650d30742a0e90225971763ebab4fe3ff6d8e97d3ef1305440b37b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1584a295e5839cf7fb34107ccb4362f61f4c09d19c82b32839dda92282d45c2bce37a768d794962a99b83cfc82c271bdf5f92c693af0dbaa576302d55c9a0b79
|
|
7
|
+
data.tar.gz: 5447dff2e9c723b47189623b378d4818ba0680fdc4f32e825a0f04d65beb14852d0e9362bc2ef00e8c7cd1814fb1b0c9661c9cf128df67cec922f9de2676c27b
|
|
@@ -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,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Api
|
|
5
|
+
module DecidimIntrospection
|
|
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,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,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,10 +7,16 @@ module Decidim
|
|
|
7
7
|
mutation(MutationType)
|
|
8
8
|
query(QueryType)
|
|
9
9
|
|
|
10
|
+
introspection(DecidimIntrospection)
|
|
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
|
|
13
17
|
|
|
18
|
+
query_analyzer AliasAnalyzer
|
|
19
|
+
|
|
14
20
|
orphan_types(Api.orphan_types)
|
|
15
21
|
end
|
|
16
22
|
end
|
|
@@ -8,6 +8,7 @@ shared_context "with a graphql class type" do
|
|
|
8
8
|
let(:type_class) { described_class }
|
|
9
9
|
let(:variables) { {} }
|
|
10
10
|
let(:root_value) { model }
|
|
11
|
+
let(:can_introspect) { Decidim::Api.enable_anonymous_introspection || current_user&.admin? }
|
|
11
12
|
|
|
12
13
|
let(:schema) do
|
|
13
14
|
klass = type_class
|
|
@@ -21,6 +22,20 @@ shared_context "with a graphql class type" do
|
|
|
21
22
|
execute_query query, variables.stringify_keys
|
|
22
23
|
end
|
|
23
24
|
|
|
25
|
+
def raise_proper_error(error)
|
|
26
|
+
code = error.dig("extensions", "code")
|
|
27
|
+
|
|
28
|
+
# Matches the error code with the Error class
|
|
29
|
+
# For instance, if the error code is NOT_FOUND_ERROR then it will raise the "Decidim::Api::Errors::NotFoundError" class
|
|
30
|
+
raise "Decidim::Api::Errors::#{code.downcase.classify}".constantize, error["message"] if %w(
|
|
31
|
+
INTROSPECTION_DISABLED_ERROR
|
|
32
|
+
TOO_MANY_ALIASES_ERROR
|
|
33
|
+
RECURSION_LIMIT_EXCEEDED_ERROR
|
|
34
|
+
).include?(code)
|
|
35
|
+
|
|
36
|
+
raise GraphQL::ExecutionError, error["message"]
|
|
37
|
+
end
|
|
38
|
+
|
|
24
39
|
def execute_query(query, variables)
|
|
25
40
|
result = schema.execute(
|
|
26
41
|
query,
|
|
@@ -28,17 +43,93 @@ shared_context "with a graphql class type" do
|
|
|
28
43
|
context: {
|
|
29
44
|
current_organization:,
|
|
30
45
|
current_user:,
|
|
31
|
-
current_component
|
|
46
|
+
current_component:,
|
|
47
|
+
can_introspect:
|
|
32
48
|
},
|
|
33
49
|
variables:
|
|
34
50
|
)
|
|
35
51
|
|
|
36
|
-
|
|
52
|
+
raise_proper_error(result["errors"].first) if result["errors"]
|
|
37
53
|
|
|
38
54
|
result["data"]
|
|
39
55
|
end
|
|
40
56
|
end
|
|
41
57
|
|
|
58
|
+
shared_examples "when the introspection is disabled" do
|
|
59
|
+
shared_examples "check introspection behavior" do
|
|
60
|
+
context "and the user is not authenticated" do
|
|
61
|
+
let!(:current_user) { nil }
|
|
62
|
+
|
|
63
|
+
it "raises an Decidim::Api::Errors::IntrospectionDisabledError" do
|
|
64
|
+
expect { response }.to raise_error(Decidim::Api::Errors::IntrospectionDisabledError, "Introspection is disabled for this request")
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
context "and the user is not an admin" do
|
|
69
|
+
let!(:current_user) { create(:user, :confirmed, organization: current_organization) }
|
|
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 an admin" do
|
|
77
|
+
let!(:current_user) { create(:user, :confirmed, :admin, organization: current_organization) }
|
|
78
|
+
|
|
79
|
+
it "runs successfully" do
|
|
80
|
+
expect { response }.not_to raise_error
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
context "and the setting is true" do
|
|
85
|
+
before do
|
|
86
|
+
allow(Decidim::Api).to receive(:enable_anonymous_introspection).and_return(true)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it "runs successfully" do
|
|
90
|
+
expect { response }.not_to raise_error
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
context "and the setting is false" do
|
|
95
|
+
before do
|
|
96
|
+
allow(Decidim::Api).to receive(:enable_anonymous_introspection).and_return(false)
|
|
97
|
+
end
|
|
98
|
+
it "raises an Decidim::Api::Errors::IntrospectionDisabledError" do
|
|
99
|
+
expect { response }.to raise_error(Decidim::Api::Errors::IntrospectionDisabledError, "Introspection is disabled for this request")
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
context "when requesting the schema introspection" do
|
|
105
|
+
let(:query) do
|
|
106
|
+
%( query { __schema { types { fields { type { fields { type { name } } } } } } } )
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it_behaves_like "check introspection behavior"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
context "when requesting the type introspection" do
|
|
113
|
+
let(:query) do
|
|
114
|
+
%( query CircularIntrospection {
|
|
115
|
+
__type(name: "User") {
|
|
116
|
+
fields {
|
|
117
|
+
type {
|
|
118
|
+
fields {
|
|
119
|
+
type {
|
|
120
|
+
name
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
} )
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it_behaves_like "check introspection behavior"
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
42
133
|
shared_context "with a graphql scalar class type" do
|
|
43
134
|
include_context "with a graphql class type"
|
|
44
135
|
|
data/lib/decidim/api/test.rb
CHANGED
data/lib/decidim/api/types.rb
CHANGED
|
@@ -2,9 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
module Decidim
|
|
4
4
|
module Api
|
|
5
|
+
autoload :AliasAnalyzer, "decidim/api/alias_analyzer"
|
|
5
6
|
autoload :QueryType, "decidim/api/query_type"
|
|
6
7
|
autoload :MutationType, "decidim/api/mutation_type"
|
|
7
8
|
autoload :Schema, "decidim/api/schema"
|
|
9
|
+
autoload :DecidimIntrospection, "decidim/api/decidim_introspection"
|
|
10
|
+
autoload :RecursionAnalyzer, "decidim/api/recursion_analyzer"
|
|
11
|
+
|
|
12
|
+
module Errors
|
|
13
|
+
autoload :IntrospectionDisabledError, "decidim/api/errors/introspection_disabled_error"
|
|
14
|
+
autoload :RecursionLimitExceededError, "decidim/api/errors/recursion_limit_exceeded_error"
|
|
15
|
+
autoload :TooManyAliasesError, "decidim/api/errors/too_many_aliases_error"
|
|
16
|
+
end
|
|
8
17
|
|
|
9
18
|
module Types
|
|
10
19
|
autoload :BaseArgument, "decidim/api/types/base_argument"
|
data/lib/decidim/api/version.rb
CHANGED
data/lib/decidim/api.rb
CHANGED
|
@@ -19,6 +19,11 @@ module Decidim
|
|
|
19
19
|
5000
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
# defines how many aliases are permitted in a query
|
|
23
|
+
config_accessor :max_aliases do
|
|
24
|
+
ENV.fetch("API_SCHEMA_MAX_ALIASES", 5).to_i
|
|
25
|
+
end
|
|
26
|
+
|
|
22
27
|
# defines the schema max_depth to configure GraphQL query max_depth
|
|
23
28
|
config_accessor :schema_max_depth do
|
|
24
29
|
15
|
|
@@ -28,6 +33,13 @@ module Decidim
|
|
|
28
33
|
%w(1 true yes).include?(ENV.fetch("DECIDIM_API_DISCLOSE_SYSTEM_VERSION", nil))
|
|
29
34
|
end
|
|
30
35
|
|
|
36
|
+
# allows anonymous introspection queries
|
|
37
|
+
# If you are not sure, leave it set to false. In this way only administrator users will be able to access the introspection query.
|
|
38
|
+
# Otherwise, anyone can access it, causing security issues.
|
|
39
|
+
config_accessor :enable_anonymous_introspection do
|
|
40
|
+
ENV.fetch("DECIDIM_API_ENABLE_ANONYMOUS_INTROSPECTION", nil) == "true"
|
|
41
|
+
end
|
|
42
|
+
|
|
31
43
|
# This declares all the types an interface or union can resolve to. This needs
|
|
32
44
|
# to be done in order to be able to have them found. This is a shortcoming of
|
|
33
45
|
# graphql-ruby and the way it deals with loading types, in combination with
|
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.30.
|
|
4
|
+
version: 0.30.5
|
|
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.30.
|
|
21
|
+
version: 0.30.5
|
|
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.30.
|
|
28
|
+
version: 0.30.5
|
|
29
29
|
- !ruby/object:Gem::Dependency
|
|
30
30
|
name: graphql
|
|
31
31
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -74,56 +74,56 @@ dependencies:
|
|
|
74
74
|
requirements:
|
|
75
75
|
- - '='
|
|
76
76
|
- !ruby/object:Gem::Version
|
|
77
|
-
version: 0.30.
|
|
77
|
+
version: 0.30.5
|
|
78
78
|
type: :development
|
|
79
79
|
prerelease: false
|
|
80
80
|
version_requirements: !ruby/object:Gem::Requirement
|
|
81
81
|
requirements:
|
|
82
82
|
- - '='
|
|
83
83
|
- !ruby/object:Gem::Version
|
|
84
|
-
version: 0.30.
|
|
84
|
+
version: 0.30.5
|
|
85
85
|
- !ruby/object:Gem::Dependency
|
|
86
86
|
name: decidim-comments
|
|
87
87
|
requirement: !ruby/object:Gem::Requirement
|
|
88
88
|
requirements:
|
|
89
89
|
- - '='
|
|
90
90
|
- !ruby/object:Gem::Version
|
|
91
|
-
version: 0.30.
|
|
91
|
+
version: 0.30.5
|
|
92
92
|
type: :development
|
|
93
93
|
prerelease: false
|
|
94
94
|
version_requirements: !ruby/object:Gem::Requirement
|
|
95
95
|
requirements:
|
|
96
96
|
- - '='
|
|
97
97
|
- !ruby/object:Gem::Version
|
|
98
|
-
version: 0.30.
|
|
98
|
+
version: 0.30.5
|
|
99
99
|
- !ruby/object:Gem::Dependency
|
|
100
100
|
name: decidim-dev
|
|
101
101
|
requirement: !ruby/object:Gem::Requirement
|
|
102
102
|
requirements:
|
|
103
103
|
- - '='
|
|
104
104
|
- !ruby/object:Gem::Version
|
|
105
|
-
version: 0.30.
|
|
105
|
+
version: 0.30.5
|
|
106
106
|
type: :development
|
|
107
107
|
prerelease: false
|
|
108
108
|
version_requirements: !ruby/object:Gem::Requirement
|
|
109
109
|
requirements:
|
|
110
110
|
- - '='
|
|
111
111
|
- !ruby/object:Gem::Version
|
|
112
|
-
version: 0.30.
|
|
112
|
+
version: 0.30.5
|
|
113
113
|
- !ruby/object:Gem::Dependency
|
|
114
114
|
name: decidim-participatory_processes
|
|
115
115
|
requirement: !ruby/object:Gem::Requirement
|
|
116
116
|
requirements:
|
|
117
117
|
- - '='
|
|
118
118
|
- !ruby/object:Gem::Version
|
|
119
|
-
version: 0.30.
|
|
119
|
+
version: 0.30.5
|
|
120
120
|
type: :development
|
|
121
121
|
prerelease: false
|
|
122
122
|
version_requirements: !ruby/object:Gem::Requirement
|
|
123
123
|
requirements:
|
|
124
124
|
- - '='
|
|
125
125
|
- !ruby/object:Gem::Version
|
|
126
|
-
version: 0.30.
|
|
126
|
+
version: 0.30.5
|
|
127
127
|
description: API engine for decidim
|
|
128
128
|
email:
|
|
129
129
|
- josepjaume@gmail.com
|
|
@@ -148,15 +148,87 @@ files:
|
|
|
148
148
|
- app/views/decidim/api/graphiql/show.html.erb
|
|
149
149
|
- app/views/layouts/decidim/api/documentation.html.erb
|
|
150
150
|
- config/assets.rb
|
|
151
|
+
- config/locales/am-ET.yml
|
|
152
|
+
- config/locales/ar.yml
|
|
153
|
+
- config/locales/bg.yml
|
|
154
|
+
- config/locales/bn-BD.yml
|
|
155
|
+
- config/locales/bs-BA.yml
|
|
156
|
+
- config/locales/ca-IT.yml
|
|
157
|
+
- config/locales/ca.yml
|
|
158
|
+
- config/locales/cs.yml
|
|
159
|
+
- config/locales/da.yml
|
|
160
|
+
- config/locales/de.yml
|
|
161
|
+
- config/locales/el.yml
|
|
162
|
+
- config/locales/en.yml
|
|
163
|
+
- config/locales/eo.yml
|
|
164
|
+
- config/locales/es-MX.yml
|
|
165
|
+
- config/locales/es-PY.yml
|
|
166
|
+
- config/locales/es.yml
|
|
167
|
+
- config/locales/et.yml
|
|
168
|
+
- config/locales/eu.yml
|
|
169
|
+
- config/locales/fa-IR.yml
|
|
170
|
+
- config/locales/fi-plain.yml
|
|
171
|
+
- config/locales/fi.yml
|
|
172
|
+
- config/locales/fr-CA.yml
|
|
173
|
+
- config/locales/fr.yml
|
|
174
|
+
- config/locales/ga-IE.yml
|
|
175
|
+
- config/locales/gl.yml
|
|
176
|
+
- config/locales/gn-PY.yml
|
|
177
|
+
- config/locales/he-IL.yml
|
|
178
|
+
- config/locales/hr.yml
|
|
179
|
+
- config/locales/hu.yml
|
|
180
|
+
- config/locales/id-ID.yml
|
|
181
|
+
- config/locales/is-IS.yml
|
|
182
|
+
- config/locales/it.yml
|
|
183
|
+
- config/locales/ja.yml
|
|
184
|
+
- config/locales/ka-GE.yml
|
|
185
|
+
- config/locales/kaa.yml
|
|
186
|
+
- config/locales/ko.yml
|
|
187
|
+
- config/locales/lb.yml
|
|
188
|
+
- config/locales/lo-LA.yml
|
|
189
|
+
- config/locales/lt.yml
|
|
190
|
+
- config/locales/lv.yml
|
|
191
|
+
- config/locales/mt.yml
|
|
192
|
+
- config/locales/nl.yml
|
|
193
|
+
- config/locales/no.yml
|
|
194
|
+
- config/locales/oc-FR.yml
|
|
195
|
+
- config/locales/om-ET.yml
|
|
196
|
+
- config/locales/pl.yml
|
|
197
|
+
- config/locales/pt-BR.yml
|
|
198
|
+
- config/locales/pt.yml
|
|
199
|
+
- config/locales/ro-RO.yml
|
|
200
|
+
- config/locales/ru.yml
|
|
201
|
+
- config/locales/si-LK.yml
|
|
202
|
+
- config/locales/sk.yml
|
|
203
|
+
- config/locales/sl.yml
|
|
204
|
+
- config/locales/so-SO.yml
|
|
205
|
+
- config/locales/sq-AL.yml
|
|
206
|
+
- config/locales/sr-CS.yml
|
|
207
|
+
- config/locales/sv.yml
|
|
208
|
+
- config/locales/sw-KE.yml
|
|
209
|
+
- config/locales/th-TH.yml
|
|
210
|
+
- config/locales/ti-ER.yml
|
|
211
|
+
- config/locales/tr-TR.yml
|
|
212
|
+
- config/locales/uk.yml
|
|
213
|
+
- config/locales/val-ES.yml
|
|
214
|
+
- config/locales/vi.yml
|
|
215
|
+
- config/locales/zh-CN.yml
|
|
216
|
+
- config/locales/zh-TW.yml
|
|
151
217
|
- config/routes.rb
|
|
152
218
|
- decidim-api.gemspec
|
|
153
219
|
- docs/usage.md
|
|
154
220
|
- lib/decidim/api.rb
|
|
221
|
+
- lib/decidim/api/alias_analyzer.rb
|
|
222
|
+
- lib/decidim/api/decidim_introspection.rb
|
|
155
223
|
- lib/decidim/api/engine.rb
|
|
224
|
+
- lib/decidim/api/errors/introspection_disabled_error.rb
|
|
225
|
+
- lib/decidim/api/errors/recursion_limit_exceeded_error.rb
|
|
226
|
+
- lib/decidim/api/errors/too_many_aliases_error.rb
|
|
156
227
|
- lib/decidim/api/graphiql-initial-query.txt
|
|
157
228
|
- lib/decidim/api/graphiql/config.rb
|
|
158
229
|
- lib/decidim/api/mutation_type.rb
|
|
159
230
|
- lib/decidim/api/query_type.rb
|
|
231
|
+
- lib/decidim/api/recursion_analyzer.rb
|
|
160
232
|
- lib/decidim/api/schema.rb
|
|
161
233
|
- lib/decidim/api/test.rb
|
|
162
234
|
- lib/decidim/api/test/component_context.rb
|