decidim-api 0.31.0.rc2 → 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.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/decidim/api/queries_controller.rb +2 -1
  3. data/config/locales/am-ET.yml +1 -0
  4. data/config/locales/ar.yml +1 -0
  5. data/config/locales/bg.yml +1 -0
  6. data/config/locales/bn-BD.yml +1 -0
  7. data/config/locales/bs-BA.yml +1 -0
  8. data/config/locales/ca-IT.yml +8 -0
  9. data/config/locales/ca.yml +8 -0
  10. data/config/locales/cs.yml +1 -0
  11. data/config/locales/da.yml +1 -0
  12. data/config/locales/de.yml +1 -0
  13. data/config/locales/el.yml +1 -0
  14. data/config/locales/en.yml +8 -0
  15. data/config/locales/eo.yml +1 -0
  16. data/config/locales/es-MX.yml +8 -0
  17. data/config/locales/es-PY.yml +8 -0
  18. data/config/locales/es.yml +8 -0
  19. data/config/locales/et.yml +1 -0
  20. data/config/locales/eu.yml +6 -0
  21. data/config/locales/fa-IR.yml +1 -0
  22. data/config/locales/fi-plain.yml +8 -0
  23. data/config/locales/fi.yml +8 -0
  24. data/config/locales/fr-CA.yml +8 -0
  25. data/config/locales/fr.yml +8 -0
  26. data/config/locales/ga-IE.yml +1 -0
  27. data/config/locales/gl.yml +1 -0
  28. data/config/locales/gn-PY.yml +1 -0
  29. data/config/locales/he-IL.yml +1 -0
  30. data/config/locales/hr.yml +1 -0
  31. data/config/locales/hu.yml +1 -0
  32. data/config/locales/id-ID.yml +1 -0
  33. data/config/locales/is-IS.yml +1 -0
  34. data/config/locales/it.yml +1 -0
  35. data/config/locales/ja.yml +8 -0
  36. data/config/locales/ka-GE.yml +1 -0
  37. data/config/locales/kaa.yml +1 -0
  38. data/config/locales/ko.yml +1 -0
  39. data/config/locales/lb.yml +1 -0
  40. data/config/locales/lo-LA.yml +1 -0
  41. data/config/locales/lt.yml +1 -0
  42. data/config/locales/lv.yml +1 -0
  43. data/config/locales/mt.yml +1 -0
  44. data/config/locales/nl.yml +1 -0
  45. data/config/locales/no.yml +6 -0
  46. data/config/locales/oc-FR.yml +1 -0
  47. data/config/locales/om-ET.yml +1 -0
  48. data/config/locales/pl.yml +1 -0
  49. data/config/locales/pt-BR.yml +8 -0
  50. data/config/locales/pt.yml +1 -0
  51. data/config/locales/ro-RO.yml +8 -0
  52. data/config/locales/ru.yml +1 -0
  53. data/config/locales/si-LK.yml +1 -0
  54. data/config/locales/sk.yml +1 -0
  55. data/config/locales/sl.yml +1 -0
  56. data/config/locales/so-SO.yml +1 -0
  57. data/config/locales/sq-AL.yml +1 -0
  58. data/config/locales/sr-CS.yml +1 -0
  59. data/config/locales/sv.yml +1 -0
  60. data/config/locales/sw-KE.yml +1 -0
  61. data/config/locales/th-TH.yml +1 -0
  62. data/config/locales/ti-ER.yml +1 -0
  63. data/config/locales/tr-TR.yml +1 -0
  64. data/config/locales/uk.yml +1 -0
  65. data/config/locales/val-ES.yml +1 -0
  66. data/config/locales/vi.yml +1 -0
  67. data/config/locales/zh-CN.yml +1 -0
  68. data/config/locales/zh-TW.yml +1 -0
  69. data/lib/decidim/api/alias_analyzer.rb +23 -0
  70. data/lib/decidim/api/engine.rb +6 -0
  71. data/lib/decidim/api/errors/introspection_disabled_error.rb +14 -0
  72. data/lib/decidim/api/errors/recursion_limit_exceeded_error.rb +14 -0
  73. data/lib/decidim/api/errors/too_many_aliases_error.rb +15 -0
  74. data/lib/decidim/api/introspection_analyzer.rb +51 -0
  75. data/lib/decidim/api/recursion_analyzer.rb +74 -0
  76. data/lib/decidim/api/schema.rb +4 -0
  77. data/lib/decidim/api/test/component_context.rb +1 -0
  78. data/lib/decidim/api/test/type_context.rb +94 -2
  79. data/lib/decidim/api/types.rb +9 -0
  80. data/lib/decidim/api/version.rb +1 -1
  81. data/lib/decidim/api.rb +14 -2
  82. metadata +84 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fd910bbc5f7451b6926870a81d2d1d2284b2f17e4848906873060bd67c879cd7
4
- data.tar.gz: 7f93a3297a2cce62c92cf6b4dba16c62b2dc25b357a31fa35e189d5dee0e9ee9
3
+ metadata.gz: a229eb60485e45b884a7db5f974fa518fee469dd3e83baf02d0d9cdb27e2e808
4
+ data.tar.gz: 6834b6467deb4c9325aeab500d77fa41ecab50f8f2d3ddb6b74de963fdbecf96
5
5
  SHA512:
6
- metadata.gz: 1bb3bd943311ca90ed893db8438bd9a1d4fd0f6cc01f38da01cae046e22403468a53e8cfe7c8c352290ae18877627af9ef979371367100b8d3067039b2f0b65f
7
- data.tar.gz: bbe3e668a5ee2f27595f230de419a3949ee100920f43556fce45d2d107c4e1b7bd95598a9c7ab41a5507467ab27e2cf359d5cef3922f8e2b8591473ac6471b88
6
+ metadata.gz: d52282c385a287f6484916c9077b687550a17d67eb47dfa4cecd6920119d5195d629439c270fb24edca6c52a0333170899016b5e07c347b3ebf029061ce5fef4
7
+ data.tar.gz: 1293a67073053c56ffdb6c25cd6bdcf8d3168216ae484ada6b11648aa170a1a461cda61dfe5f3b95ed9a6dde5a7a1d62d444e510a4b7d8e5bf29fd4a6e3cf2e9
@@ -30,7 +30,8 @@ module Decidim
30
30
  {
31
31
  current_organization:,
32
32
  current_user: api_user,
33
- scopes: api_scopes
33
+ scopes: api_scopes,
34
+ can_introspect: Decidim::Api.enable_anonymous_introspection || api_user&.admin?
34
35
  }
35
36
  end
36
37
 
@@ -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,6 @@
1
+ ---
2
+ eu:
3
+ decidim:
4
+ api:
5
+ errors:
6
+ too_many_aliases_error: Ezizen gehiegi erabiltzen dira. %{size} ezizen erabili dituzu, baina %{limit} onartuta daude.
@@ -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,8 @@
1
+ ---
2
+ ja:
3
+ decidim:
4
+ api:
5
+ errors:
6
+ introspection_disabled: このリクエストのイントロスペクションは無効です
7
+ recursion_limit_exceeded_error: クエリで再帰が検出された回数が多すぎます
8
+ too_many_aliases_error: エイリアスが多すぎます。 %{size} 件のエイリアスを使用していますが、許可されているのは %{limit} 件までです。
@@ -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,6 @@
1
+ ---
2
+ "no":
3
+ decidim:
4
+ api:
5
+ errors:
6
+ recursion_limit_exceeded_error: For mange rekursjoner oppdaget i spørringen
@@ -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
@@ -41,6 +41,12 @@ module Decidim
41
41
  end
42
42
  end
43
43
 
44
+ initializer "decidim_api.data_migrate", after: "decidim_core.data_migrate" do
45
+ DataMigrate.configure do |config|
46
+ config.data_migrations_path << root.join("db/data").to_s
47
+ end
48
+ end
49
+
44
50
  initializer "decidim_api.shakapacker.assets_path" do
45
51
  Decidim.register_assets_path File.expand_path("app/packs", root)
46
52
  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
@@ -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
@@ -2,6 +2,7 @@
2
2
 
3
3
  shared_context "with a graphql decidim component" do
4
4
  include_context "with a graphql class type"
5
+ include_examples "when the introspection is disabled"
5
6
 
6
7
  let(:schema) { Decidim::Api::Schema }
7
8
 
@@ -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
- raise StandardError, result["errors"].map { |e| e["message"] }.join(", ") if result["errors"]
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
 
@@ -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"
@@ -4,7 +4,7 @@ module Decidim
4
4
  # This holds the decidim-api version.
5
5
  module Api
6
6
  def self.version
7
- "0.31.0.rc2"
7
+ "0.31.1"
8
8
  end
9
9
  end
10
10
  end
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
- # Public Setting that can make the API authentication necessary in order to
36
- # access it.
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.0.rc2
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: 2025-10-28 00:00:00.000000000 Z
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.0.rc2
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.0.rc2
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.0.rc2
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.0.rc2
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.0.rc2
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.0.rc2
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.0.rc2
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.0.rc2
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.0.rc2
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.0.rc2
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