tina4ruby 3.11.1 → 3.11.2
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/lib/tina4/graphql.rb +91 -1
- data/lib/tina4/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ef9466f8965eb7728b8aadecd725236011298269966d3775c9f11dc1aa9b0c62
|
|
4
|
+
data.tar.gz: 11f5cc6ac83ad16b63a23068476517e0babe7db60f793942e2fa4b7048f12894
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d214fe2b2e79c28232fec6ed03af2d352b70854e66a1611535ad49bd6c79059110579a6d85ab2c8fdc0de8db215abcfae8fe1332d4398985a53c9a3bb4cc5ede
|
|
7
|
+
data.tar.gz: 5095de7d8683d27ef009859f3e9272e50be8eece9eeea94a7abf62332028cbf9a4aaabf1d3fd6468c57bfbfeca13d2b276703ea55d89d3190343686fd46d5765
|
data/lib/tina4/graphql.rb
CHANGED
|
@@ -651,14 +651,29 @@ module Tina4
|
|
|
651
651
|
field_name = selection[:name]
|
|
652
652
|
output_name = selection[:alias] || field_name
|
|
653
653
|
|
|
654
|
+
# Check directives (@skip, @include, @auth, @role, @guest)
|
|
655
|
+
return unless check_directives(selection[:directives] || [], variables, context)
|
|
656
|
+
|
|
654
657
|
# Resolve arguments (substitute variables)
|
|
655
658
|
args = resolve_args(selection[:arguments], variables)
|
|
656
659
|
|
|
657
660
|
field_def = fields[field_name]
|
|
658
661
|
|
|
662
|
+
# Input validation
|
|
663
|
+
if field_def && field_def[:args]
|
|
664
|
+
validation_errors = validate_args(args, field_def[:args], field_name)
|
|
665
|
+
if validation_errors.any?
|
|
666
|
+
errors.concat(validation_errors)
|
|
667
|
+
data[output_name] = nil
|
|
668
|
+
return
|
|
669
|
+
end
|
|
670
|
+
end
|
|
671
|
+
|
|
659
672
|
begin
|
|
660
673
|
if field_def && field_def[:resolve]
|
|
661
|
-
|
|
674
|
+
# Inject sub-selections into context for DataLoader/eager-loading
|
|
675
|
+
ctx = context.merge("__selections" => (selection[:selection_set] || []))
|
|
676
|
+
value = field_def[:resolve].call(parent, args, ctx)
|
|
662
677
|
elsif parent.is_a?(Hash)
|
|
663
678
|
value = parent[field_name] || parent[field_name.to_sym]
|
|
664
679
|
else
|
|
@@ -705,6 +720,81 @@ module Tina4
|
|
|
705
720
|
result
|
|
706
721
|
end
|
|
707
722
|
|
|
723
|
+
# Check directives: @skip, @include, @auth, @role, @guest.
|
|
724
|
+
# Returns true if the field should be included.
|
|
725
|
+
def check_directives(directives, variables, context = {})
|
|
726
|
+
directives.each do |d|
|
|
727
|
+
val = d[:arguments]&.dig("if")
|
|
728
|
+
val = variables[val[:name]] if val.is_a?(Hash) && val[:kind] == :variable
|
|
729
|
+
|
|
730
|
+
return false if d[:name] == "skip" && val
|
|
731
|
+
return false if d[:name] == "include" && !val
|
|
732
|
+
|
|
733
|
+
# Auth: @auth — requires authenticated user
|
|
734
|
+
return false if d[:name] == "auth" && !context["user"]
|
|
735
|
+
|
|
736
|
+
# Auth: @role(role: "admin") — requires specific role
|
|
737
|
+
if d[:name] == "role"
|
|
738
|
+
required = d[:arguments]&.dig("role")
|
|
739
|
+
user = context["user"]
|
|
740
|
+
actual = user.is_a?(Hash) ? (user["role"] || user[:role]) : nil
|
|
741
|
+
actual ||= context["role"]
|
|
742
|
+
return false if required.nil? || actual != required
|
|
743
|
+
end
|
|
744
|
+
|
|
745
|
+
# Auth: @guest — only for unauthenticated
|
|
746
|
+
return false if d[:name] == "guest" && context["user"]
|
|
747
|
+
end
|
|
748
|
+
true
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
# Validate resolved args against declared types.
|
|
752
|
+
def validate_args(args, arg_configs, field_name)
|
|
753
|
+
errors = []
|
|
754
|
+
arg_configs.each do |arg_name, declared_type|
|
|
755
|
+
value = args[arg_name]
|
|
756
|
+
is_non_null = declared_type.to_s.end_with?("!")
|
|
757
|
+
base_type = declared_type.to_s.gsub(/[!\[\]]/, "").strip
|
|
758
|
+
|
|
759
|
+
if is_non_null && (value.nil? || value == "")
|
|
760
|
+
errors << {
|
|
761
|
+
"message" => "Argument '#{arg_name}' on field '#{field_name}' is required (type: #{declared_type})",
|
|
762
|
+
"path" => [field_name]
|
|
763
|
+
}
|
|
764
|
+
next
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
next if value.nil?
|
|
768
|
+
|
|
769
|
+
if %w[Int Float Boolean String ID].include?(base_type)
|
|
770
|
+
unless coerce_check(value, base_type)
|
|
771
|
+
errors << {
|
|
772
|
+
"message" => "Argument '#{arg_name}' on field '#{field_name}' expected type #{base_type}, got #{value.class}",
|
|
773
|
+
"path" => [field_name]
|
|
774
|
+
}
|
|
775
|
+
end
|
|
776
|
+
end
|
|
777
|
+
end
|
|
778
|
+
errors
|
|
779
|
+
end
|
|
780
|
+
|
|
781
|
+
def coerce_check(value, type_name)
|
|
782
|
+
case type_name
|
|
783
|
+
when "String", "ID"
|
|
784
|
+
value.is_a?(String) || value.is_a?(Numeric) || value.is_a?(Symbol)
|
|
785
|
+
when "Int"
|
|
786
|
+
return false if value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
|
787
|
+
value.is_a?(Integer) || (value.is_a?(String) && value.match?(/\A-?\d+\z/))
|
|
788
|
+
when "Float"
|
|
789
|
+
return false if value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
|
790
|
+
value.is_a?(Numeric) || (value.is_a?(String) && value.match?(/\A-?\d+(\.\d+)?\z/))
|
|
791
|
+
when "Boolean"
|
|
792
|
+
value.is_a?(TrueClass) || value.is_a?(FalseClass) || [0, 1, "true", "false"].include?(value)
|
|
793
|
+
else
|
|
794
|
+
true
|
|
795
|
+
end
|
|
796
|
+
end
|
|
797
|
+
|
|
708
798
|
def resolve_args(args, variables)
|
|
709
799
|
return {} unless args
|
|
710
800
|
resolved = {}
|
data/lib/tina4/version.rb
CHANGED