graphql-activerecord 0.10.0 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +21 -0
- data/.rubocop.yml +125 -0
- data/.rubocop_todo.yml +48 -0
- data/CHANGELOG.md +9 -3
- data/Gemfile +1 -0
- data/Rakefile +2 -1
- data/bin/rubocop +17 -0
- data/bin/ruby-parse +17 -0
- data/bin/ruby-rewrite +17 -0
- data/graphql-activerecord.gemspec +10 -8
- data/lib/graphql/activerecord.rb +2 -2
- data/lib/graphql/models/active_record_extension.rb +5 -4
- data/lib/graphql/models/association_load_request.rb +3 -3
- data/lib/graphql/models/attribute_loader.rb +1 -0
- data/lib/graphql/models/backed_by_model.rb +7 -6
- data/lib/graphql/models/database_types.rb +1 -0
- data/lib/graphql/models/definer.rb +5 -4
- data/lib/graphql/models/definition_helpers.rb +6 -6
- data/lib/graphql/models/definition_helpers/associations.rb +23 -22
- data/lib/graphql/models/definition_helpers/attributes.rb +4 -3
- data/lib/graphql/models/hash_combiner.rb +1 -0
- data/lib/graphql/models/helpers.rb +1 -0
- data/lib/graphql/models/middleware.rb +1 -0
- data/lib/graphql/models/monkey_patches/graphql_query_context.rb +1 -0
- data/lib/graphql/models/mutation_field_map.rb +11 -10
- data/lib/graphql/models/mutation_helpers/apply_changes.rb +11 -11
- data/lib/graphql/models/mutation_helpers/authorization.rb +1 -0
- data/lib/graphql/models/mutation_helpers/print_input_fields.rb +3 -2
- data/lib/graphql/models/mutation_helpers/validation.rb +7 -8
- data/lib/graphql/models/mutation_helpers/validation_error.rb +6 -7
- data/lib/graphql/models/mutator.rb +6 -5
- data/lib/graphql/models/promise_relation_connection.rb +1 -0
- data/lib/graphql/models/reflection.rb +4 -3
- data/lib/graphql/models/relation_load_request.rb +2 -3
- data/lib/graphql/models/relation_loader.rb +3 -3
- data/lib/graphql/models/version.rb +2 -1
- metadata +22 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0898e348a67f2ffea03d24b07bad53f4ee4e303c
|
4
|
+
data.tar.gz: fe1f6ef15222a43dabb77ee545a8fbb22bf5509c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d224b60c12152281cba045789a6c0122740b65368ea14e9327ca794a87db2ba82a1415b8ab63cff0565b04be8cf803004c7ad5c7f3b55d0468d31bf24e68eb82
|
7
|
+
data.tar.gz: 3568541b0befd15885528e980a63a18295764ccc35f47207400d0d48439a3ee42d1121efa143303f3de2fca7941def87c414ca420bb2208c082ef210a5aaf79f
|
@@ -0,0 +1,21 @@
|
|
1
|
+
version: 2
|
2
|
+
jobs:
|
3
|
+
build:
|
4
|
+
working_directory: /graphql-activerecord
|
5
|
+
docker:
|
6
|
+
- image: ruby:2.3.1
|
7
|
+
steps:
|
8
|
+
- checkout
|
9
|
+
|
10
|
+
# Install dependencies
|
11
|
+
- restore_cache:
|
12
|
+
key: bundler-{{ checksum "graphql-activerecord.gemspec" }}
|
13
|
+
- run: bundle install --path=vendor/gems
|
14
|
+
- save_cache:
|
15
|
+
key: bundler-{{ checksum "graphql-activerecord.gemspec" }}
|
16
|
+
paths:
|
17
|
+
- vendor/gems
|
18
|
+
|
19
|
+
# Tests
|
20
|
+
- run: rubocop
|
21
|
+
- run: rspec
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
inherit_from: .rubocop_todo.yml
|
2
|
+
AllCops:
|
3
|
+
TargetRubyVersion: 2.3
|
4
|
+
Exclude:
|
5
|
+
- bin/**/*
|
6
|
+
- vendor/gems/**/*
|
7
|
+
|
8
|
+
Lint/EndAlignment:
|
9
|
+
EnforcedStyleAlignWith: variable
|
10
|
+
Enabled: false
|
11
|
+
|
12
|
+
Lint/UnusedBlockArgument:
|
13
|
+
AllowUnusedKeywordArguments: true
|
14
|
+
|
15
|
+
Lint/UnusedMethodArgument:
|
16
|
+
AllowUnusedKeywordArguments: true
|
17
|
+
IgnoreEmptyMethods: true
|
18
|
+
|
19
|
+
Metrics/BlockLength:
|
20
|
+
CountComments: false
|
21
|
+
Max: 96
|
22
|
+
|
23
|
+
Metrics/ClassLength:
|
24
|
+
Max: 337
|
25
|
+
|
26
|
+
Metrics/CyclomaticComplexity:
|
27
|
+
Max: 13
|
28
|
+
|
29
|
+
Metrics/MethodLength:
|
30
|
+
CountComments: false
|
31
|
+
|
32
|
+
Metrics/ModuleLength:
|
33
|
+
CountComments: false
|
34
|
+
Max: 222
|
35
|
+
|
36
|
+
Metrics/PerceivedComplexity:
|
37
|
+
Max: 18
|
38
|
+
|
39
|
+
Style/Documentation:
|
40
|
+
Enabled: false
|
41
|
+
|
42
|
+
Style/DoubleNegation:
|
43
|
+
Enabled: false
|
44
|
+
|
45
|
+
Style/ExtraSpacing:
|
46
|
+
AllowForAlignment: false
|
47
|
+
|
48
|
+
Style/FrozenStringLiteralComment:
|
49
|
+
EnforcedStyle: always
|
50
|
+
|
51
|
+
Style/GlobalVars:
|
52
|
+
AllowedVariables: [$redis_pool]
|
53
|
+
|
54
|
+
Style/HashSyntax:
|
55
|
+
EnforcedStyle: ruby19_no_mixed_keys
|
56
|
+
|
57
|
+
Style/IfUnlessModifier:
|
58
|
+
Enabled: false
|
59
|
+
|
60
|
+
Style/IndentArray:
|
61
|
+
EnforcedStyle: consistent
|
62
|
+
|
63
|
+
Style/IndentHash:
|
64
|
+
EnforcedStyle: consistent
|
65
|
+
|
66
|
+
Style/Lambda:
|
67
|
+
EnforcedStyle: literal
|
68
|
+
|
69
|
+
Style/MultilineMethodCallIndentation:
|
70
|
+
Enabled: false
|
71
|
+
|
72
|
+
Style/NegatedIf:
|
73
|
+
Enabled: false
|
74
|
+
|
75
|
+
Style/NegatedWhile:
|
76
|
+
Enabled: false
|
77
|
+
|
78
|
+
Style/StringLiterals:
|
79
|
+
Enabled: false
|
80
|
+
|
81
|
+
Style/TrailingCommaInLiteral:
|
82
|
+
EnforcedStyleForMultiline: consistent_comma
|
83
|
+
|
84
|
+
Style/Attr:
|
85
|
+
Enabled: false
|
86
|
+
|
87
|
+
Style/SpaceInLambdaLiteral:
|
88
|
+
EnforcedStyle: require_space
|
89
|
+
|
90
|
+
Style/AlignParameters:
|
91
|
+
EnforcedStyle: with_fixed_indentation
|
92
|
+
|
93
|
+
Style/WordArray:
|
94
|
+
EnforcedStyle: percent
|
95
|
+
MinSize: 5
|
96
|
+
|
97
|
+
Style/StringLiteralsInInterpolation:
|
98
|
+
Enabled: false
|
99
|
+
|
100
|
+
Style/TrailingUnderscoreVariable:
|
101
|
+
Enabled: false
|
102
|
+
|
103
|
+
Style/TernaryParentheses:
|
104
|
+
Enabled: false
|
105
|
+
|
106
|
+
Style/PredicateName:
|
107
|
+
Enabled: false
|
108
|
+
|
109
|
+
Style/NumericPredicate:
|
110
|
+
Enabled: false
|
111
|
+
|
112
|
+
Style/NumericLiterals:
|
113
|
+
MinDigits: 5
|
114
|
+
|
115
|
+
Style/ClassAndModuleChildren:
|
116
|
+
Enabled: false
|
117
|
+
|
118
|
+
Style/CaseIndentation:
|
119
|
+
EnforcedStyle: end
|
120
|
+
|
121
|
+
Style/BracesAroundHashParameters:
|
122
|
+
Enabled: false
|
123
|
+
|
124
|
+
Style/IndentationWidth:
|
125
|
+
Enabled: false
|
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config`
|
3
|
+
# on 2017-03-12 22:31:48 -0500 using RuboCop version 0.47.1.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
8
|
+
|
9
|
+
# Offense count: 3
|
10
|
+
Lint/UselessAssignment:
|
11
|
+
Exclude:
|
12
|
+
- 'lib/graphql/models/definition_helpers.rb'
|
13
|
+
- 'lib/graphql/models/definition_helpers/attributes.rb'
|
14
|
+
- 'lib/graphql/models/mutation_helpers/print_input_fields.rb'
|
15
|
+
|
16
|
+
# Offense count: 5
|
17
|
+
Metrics/AbcSize:
|
18
|
+
Max: 83
|
19
|
+
|
20
|
+
# Offense count: 1
|
21
|
+
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
22
|
+
# URISchemes: http, https
|
23
|
+
Metrics/LineLength:
|
24
|
+
Max: 201
|
25
|
+
|
26
|
+
# Offense count: 3
|
27
|
+
# Configuration parameters: CountComments.
|
28
|
+
Metrics/MethodLength:
|
29
|
+
Max: 56
|
30
|
+
|
31
|
+
# Offense count: 5
|
32
|
+
# Configuration parameters: CountKeywordArgs.
|
33
|
+
Metrics/ParameterLists:
|
34
|
+
Max: 9
|
35
|
+
|
36
|
+
# Offense count: 5
|
37
|
+
# Configuration parameters: MinBodyLength.
|
38
|
+
Style/GuardClause:
|
39
|
+
Exclude:
|
40
|
+
- 'lib/graphql/models/association_load_request.rb'
|
41
|
+
- 'lib/graphql/models/mutation_field_map.rb'
|
42
|
+
- 'lib/graphql/models/mutation_helpers/apply_changes.rb'
|
43
|
+
- 'lib/graphql/models/mutation_helpers/validation.rb'
|
44
|
+
|
45
|
+
# Offense count: 2
|
46
|
+
Style/IdenticalConditionalBranches:
|
47
|
+
Exclude:
|
48
|
+
- 'lib/graphql/models/definition_helpers.rb'
|
data/CHANGELOG.md
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
# 0.11.0
|
4
|
+
Breaking Bug Fix: Turns out that 0.10.0 was _supposed_ to introduce (non)nullability on attributes, but it didn’t quite work. That’s
|
5
|
+
fixed in 0.11.0 (and, I _really_ need to write some tests for this gem).
|
6
|
+
|
3
7
|
# 0.10.0
|
4
8
|
There are a few breaking changes:
|
5
9
|
- Added automatic nullability checking for attributes. It’s enabled by default; see the README for more info.
|
6
10
|
- The gem now assumes that the object types for your models are called "ModelNameType" instead of "ModelNameGraph",
|
7
11
|
to bring it more in line with common practice. You can get the old behavior by adding this to an initializer:
|
8
|
-
|
12
|
+
|
9
13
|
```ruby
|
10
14
|
GraphQL::Models.model_to_graphql_type -> (model_class) { "#{model_class.name}Graph".safe_constantize }
|
11
15
|
```
|
@@ -13,9 +17,11 @@ There are a few breaking changes:
|
|
13
17
|
- Fixed a bug with the `has_many_connection` helper, which deserves some explanation. This helper constructs a
|
14
18
|
connection field that returns an ActiveRecord relation. There isn't an easy way to inject functionality into the resolvers
|
15
19
|
that are used by connections (to my knowledge) - eg, by using middleware - so this helper had some GoCo-specific code
|
16
|
-
baked into it, which probably caused odd errors about an undefined constant `GraphSupport` whenever it was used. I can’t
|
20
|
+
baked into it, which probably caused odd errors about an undefined constant `GraphSupport` whenever it was used. ~~I can’t
|
17
21
|
quite remove that functionality yet, but I did take it one step closer by having the code first check to see if the constant
|
18
|
-
was defined, and bypass it if it’s not
|
22
|
+
was defined, and bypass it if it’s not.~~ That code has been removed from the gem now!
|
23
|
+
|
24
|
+
- Fixed a bug where the HashCombiner would sometimes not merge hashes together (if their keys were sorted differently)
|
19
25
|
|
20
26
|
## 0.9.0
|
21
27
|
- Support for graphql version 1.2.1 and higher, but it no longer works with 0.x versions
|
data/Gemfile
CHANGED
data/Rakefile
CHANGED
data/bin/rubocop
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
#
|
4
|
+
# This file was generated by Bundler.
|
5
|
+
#
|
6
|
+
# The application 'rubocop' is installed as part of a gem, and
|
7
|
+
# this file is here to facilitate running it.
|
8
|
+
#
|
9
|
+
|
10
|
+
require "pathname"
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
12
|
+
Pathname.new(__FILE__).realpath)
|
13
|
+
|
14
|
+
require "rubygems"
|
15
|
+
require "bundler/setup"
|
16
|
+
|
17
|
+
load Gem.bin_path("rubocop", "rubocop")
|
data/bin/ruby-parse
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
#
|
4
|
+
# This file was generated by Bundler.
|
5
|
+
#
|
6
|
+
# The application 'ruby-parse' is installed as part of a gem, and
|
7
|
+
# this file is here to facilitate running it.
|
8
|
+
#
|
9
|
+
|
10
|
+
require "pathname"
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
12
|
+
Pathname.new(__FILE__).realpath)
|
13
|
+
|
14
|
+
require "rubygems"
|
15
|
+
require "bundler/setup"
|
16
|
+
|
17
|
+
load Gem.bin_path("parser", "ruby-parse")
|
data/bin/ruby-rewrite
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
#
|
4
|
+
# This file was generated by Bundler.
|
5
|
+
#
|
6
|
+
# The application 'ruby-rewrite' is installed as part of a gem, and
|
7
|
+
# this file is here to facilitate running it.
|
8
|
+
#
|
9
|
+
|
10
|
+
require "pathname"
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
12
|
+
Pathname.new(__FILE__).realpath)
|
13
|
+
|
14
|
+
require "rubygems"
|
15
|
+
require "bundler/setup"
|
16
|
+
|
17
|
+
load Gem.bin_path("parser", "ruby-rewrite")
|
@@ -1,22 +1,23 @@
|
|
1
1
|
# coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
2
3
|
lib = File.expand_path('../lib', __FILE__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
5
|
require 'graphql/models/version'
|
5
6
|
|
6
7
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name
|
8
|
-
spec.version
|
9
|
-
spec.authors
|
10
|
-
spec.email
|
8
|
+
spec.name = "graphql-activerecord"
|
9
|
+
spec.version = GraphQL::Models::VERSION
|
10
|
+
spec.authors = ["Ryan Foster"]
|
11
|
+
spec.email = ["theorygeek@gmail.com"]
|
11
12
|
|
12
13
|
spec.summary = "ActiveRecord helpers for GraphQL + Relay"
|
13
14
|
spec.description = "Build Relay-compatible GraphQL schemas based on ActiveRecord models"
|
14
|
-
spec.homepage =
|
15
|
+
spec.homepage = "http://github.com/goco-inc/graphql-activerecord"
|
15
16
|
spec.license = "MIT"
|
16
17
|
|
17
|
-
spec.files
|
18
|
-
spec.bindir
|
19
|
-
spec.executables
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
|
+
spec.bindir = "exe"
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
21
|
spec.require_paths = ["lib"]
|
21
22
|
|
22
23
|
spec.add_runtime_dependency "activesupport", ">= 4.2", '< 5'
|
@@ -28,4 +29,5 @@ Gem::Specification.new do |spec|
|
|
28
29
|
spec.add_development_dependency "rake", "~> 10.0"
|
29
30
|
spec.add_development_dependency "rspec", "~> 3.0"
|
30
31
|
spec.add_development_dependency "pry"
|
32
|
+
spec.add_development_dependency "rubocop", '~> 0.47.1'
|
31
33
|
end
|
data/lib/graphql/activerecord.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'active_support'
|
2
3
|
require 'active_record'
|
3
4
|
require 'graphql'
|
@@ -34,7 +35,6 @@ require 'graphql/models/mutation_field_map'
|
|
34
35
|
require 'graphql/models/backed_by_model'
|
35
36
|
require 'graphql/models/mutator'
|
36
37
|
|
37
|
-
|
38
38
|
module GraphQL
|
39
39
|
module Models
|
40
40
|
class << self
|
@@ -94,7 +94,7 @@ module GraphQL
|
|
94
94
|
|
95
95
|
def self.get_graphql_type!(model_class)
|
96
96
|
type = get_graphql_type(model_class)
|
97
|
-
|
97
|
+
raise "Could not locate GraphQL type for model #{model_class}" if type.nil?
|
98
98
|
type
|
99
99
|
end
|
100
100
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module GraphQL
|
2
3
|
module Models
|
3
4
|
module ActiveRecordExtension
|
@@ -38,14 +39,14 @@ module GraphQL
|
|
38
39
|
name = "#{self.name}#{attribute.to_s.classify}"
|
39
40
|
description = "#{attribute.to_s.titleize} field on #{self.name.titleize}"
|
40
41
|
|
41
|
-
if defined_enums.include?(attribute.to_s)
|
42
|
-
|
42
|
+
values = if defined_enums.include?(attribute.to_s)
|
43
|
+
defined_enums[attribute.to_s].keys
|
43
44
|
else
|
44
|
-
|
45
|
+
Reflection.possible_values(self, attribute)
|
45
46
|
end
|
46
47
|
|
47
48
|
if values.nil?
|
48
|
-
|
49
|
+
raise ArgumentError, "Could not auto-detect the values for enum #{attribute} on #{self.name}"
|
49
50
|
end
|
50
51
|
|
51
52
|
type = GraphQL::EnumType.define do
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module GraphQL
|
2
3
|
module Models
|
3
4
|
class AssociationLoadRequest
|
@@ -9,7 +10,7 @@ module GraphQL
|
|
9
10
|
@context = context
|
10
11
|
|
11
12
|
if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
12
|
-
|
13
|
+
raise ArgumentError, "You cannot batch-load a has_many :through association. Instead, load each association individually."
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
@@ -33,7 +34,7 @@ module GraphQL
|
|
33
34
|
#################################################################
|
34
35
|
|
35
36
|
def target_class
|
36
|
-
|
37
|
+
if reflection.polymorphic?
|
37
38
|
base_model.send(reflection.foreign_type).constantize
|
38
39
|
else
|
39
40
|
reflection.klass
|
@@ -49,7 +50,6 @@ module GraphQL
|
|
49
50
|
def reflection
|
50
51
|
association.reflection
|
51
52
|
end
|
52
|
-
|
53
53
|
end
|
54
54
|
end
|
55
55
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module GraphQL
|
2
3
|
module Models
|
3
4
|
class BackedByModel
|
@@ -44,10 +45,10 @@ module GraphQL
|
|
44
45
|
name: name,
|
45
46
|
nullable: nullable,
|
46
47
|
description: description,
|
47
|
-
deprecation_reason: deprecation_reason
|
48
|
+
deprecation_reason: deprecation_reason,
|
48
49
|
}
|
49
50
|
|
50
|
-
DefinitionHelpers.define_attribute(@graph_type, @base_model_type, @model_type, @path, attribute, @object_to_model, options,
|
51
|
+
DefinitionHelpers.define_attribute(@graph_type, @base_model_type, @model_type, @path, attribute, @object_to_model, options, @detect_nulls, &block)
|
51
52
|
end
|
52
53
|
|
53
54
|
# Flattens an associated model into the graph type, allowing to you adds its attributes as if they existed on the parent model.
|
@@ -63,7 +64,7 @@ module GraphQL
|
|
63
64
|
name: name,
|
64
65
|
nullable: nullable,
|
65
66
|
description: description,
|
66
|
-
deprecation_reason: deprecation_reason
|
67
|
+
deprecation_reason: deprecation_reason,
|
67
68
|
}
|
68
69
|
|
69
70
|
DefinitionHelpers.define_has_one(@graph_type, @base_model_type, @model_type, @path, association, @object_to_model, options, @detect_nulls)
|
@@ -76,7 +77,7 @@ module GraphQL
|
|
76
77
|
name: name,
|
77
78
|
nullable: nullable,
|
78
79
|
description: description,
|
79
|
-
deprecation_reason: deprecation_reason
|
80
|
+
deprecation_reason: deprecation_reason,
|
80
81
|
})
|
81
82
|
|
82
83
|
DefinitionHelpers.define_has_many_connection(@graph_type, @base_model_type, @model_type, @path, association, @object_to_model, options, @detect_nulls)
|
@@ -90,7 +91,7 @@ module GraphQL
|
|
90
91
|
type: type,
|
91
92
|
nullable: nullable,
|
92
93
|
description: description,
|
93
|
-
deprecation_reason: deprecation_reason
|
94
|
+
deprecation_reason: deprecation_reason,
|
94
95
|
}
|
95
96
|
|
96
97
|
DefinitionHelpers.define_has_many_array(@graph_type, @base_model_type, @model_type, @path, association, @object_to_model, options, @detect_nulls)
|
@@ -107,7 +108,7 @@ module GraphQL
|
|
107
108
|
path: @path,
|
108
109
|
base_model_type: @base_model_type,
|
109
110
|
model_type: @model_type,
|
110
|
-
object_to_base_model: @object_to_model
|
111
|
+
object_to_base_model: @object_to_model,
|
111
112
|
})
|
112
113
|
|
113
114
|
defined_field
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
# This is a helper class. It lets you build simple DSL's. Methods called against the class are
|
2
3
|
# converted into attributes in a hash.
|
3
4
|
module GraphQL
|
@@ -7,12 +8,12 @@ module GraphQL
|
|
7
8
|
@values = {}
|
8
9
|
methods.each do |m|
|
9
10
|
define_singleton_method(m) do |*args|
|
10
|
-
if args.blank?
|
11
|
-
|
11
|
+
@values[m] = if args.blank?
|
12
|
+
nil
|
12
13
|
elsif args.length == 1
|
13
|
-
|
14
|
+
args[0]
|
14
15
|
else
|
15
|
-
|
16
|
+
args
|
16
17
|
end
|
17
18
|
end
|
18
19
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'ostruct'
|
2
3
|
|
3
4
|
module GraphQL
|
@@ -10,16 +11,16 @@ module GraphQL
|
|
10
11
|
# Returns a promise that will eventually resolve to the model that is at the end of the path
|
11
12
|
def self.load_and_traverse(current_model, path, context)
|
12
13
|
cache_model(context, current_model)
|
13
|
-
return Promise.resolve(current_model) if path.
|
14
|
+
return Promise.resolve(current_model) if path.empty? || current_model.nil?
|
14
15
|
|
15
16
|
association = current_model.association(path[0])
|
16
17
|
|
17
|
-
while path.
|
18
|
+
while !path.empty? && (association.loaded? || attempt_cache_load(current_model, association, context))
|
18
19
|
current_model = association.target
|
19
20
|
path = path[1..-1]
|
20
21
|
cache_model(context, current_model)
|
21
22
|
|
22
|
-
return Promise.resolve(current_model) if path.
|
23
|
+
return Promise.resolve(current_model) if path.empty? || current_model.nil?
|
23
24
|
|
24
25
|
association = current_model.association(path[0])
|
25
26
|
end
|
@@ -31,7 +32,6 @@ module GraphQL
|
|
31
32
|
# First step, load the :through association (ie, the :open_enrollments)
|
32
33
|
through = association.reflection.options[:through]
|
33
34
|
load_and_traverse(current_model, [through], context).then do |intermediate_models|
|
34
|
-
|
35
35
|
# Now, for each of the intermediate models (OpenEnrollment), load the source association (:health_line)
|
36
36
|
sources = intermediate_models.map do |im|
|
37
37
|
load_and_traverse(im, [association.reflection.source_reflection_name], context)
|
@@ -107,14 +107,14 @@ module GraphQL
|
|
107
107
|
association.set_inverse_instance(target) unless target.nil?
|
108
108
|
end
|
109
109
|
|
110
|
-
def self.traverse_path(base_model, path,
|
110
|
+
def self.traverse_path(base_model, path, _context)
|
111
111
|
model = base_model
|
112
112
|
path.each do |segment|
|
113
113
|
return nil unless model
|
114
114
|
model = model.public_send(segment)
|
115
115
|
end
|
116
116
|
|
117
|
-
|
117
|
+
model
|
118
118
|
end
|
119
119
|
|
120
120
|
# Stores metadata about GraphQL fields that are available on this model's GraphQL type.
|
@@ -1,11 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module GraphQL
|
2
3
|
module Models
|
3
4
|
module DefinitionHelpers
|
4
5
|
def self.define_proxy(graph_type, base_model_type, model_type, path, association, object_to_model, detect_nulls, &block)
|
5
6
|
reflection = model_type.reflect_on_association(association)
|
6
|
-
raise ArgumentError
|
7
|
-
raise ArgumentError
|
8
|
-
raise ArgumentError
|
7
|
+
raise ArgumentError, "Association #{association} wasn't found on model #{model_type.name}" unless reflection
|
8
|
+
raise ArgumentError, "Cannot proxy to polymorphic association #{association} on model #{model_type.name}" if reflection.polymorphic?
|
9
|
+
raise ArgumentError, "Cannot proxy to #{reflection.macro} association #{association} on model #{model_type.name}" unless [:has_one, :belongs_to].include?(reflection.macro)
|
9
10
|
|
10
11
|
return unless block_given?
|
11
12
|
|
@@ -34,7 +35,7 @@ module GraphQL
|
|
34
35
|
valid_types = Reflection.possible_values(model_type, reflection.foreign_type)
|
35
36
|
|
36
37
|
if valid_types.blank?
|
37
|
-
|
38
|
+
raise ArgumentError, "Cannot include polymorphic #{reflection.name} association on model #{model_type.name}, because it does not define an inclusion validator on #{reflection.foreign_type}"
|
38
39
|
end
|
39
40
|
|
40
41
|
graph_types = valid_types.map { |t| GraphQL::Models.get_graphql_type(t) }.compact
|
@@ -54,8 +55,8 @@ module GraphQL
|
|
54
55
|
def self.define_has_one(graph_type, base_model_type, model_type, path, association, object_to_model, options, detect_nulls)
|
55
56
|
reflection = model_type.reflect_on_association(association)
|
56
57
|
|
57
|
-
|
58
|
-
|
58
|
+
raise ArgumentError, "Association #{association} wasn't found on model #{model_type.name}" unless reflection
|
59
|
+
raise ArgumentError, "Cannot include #{reflection.macro} association #{association} on model #{model_type.name} with has_one" unless [:has_one, :belongs_to].include?(reflection.macro)
|
59
60
|
|
60
61
|
# Define the field for the association itself
|
61
62
|
|
@@ -70,7 +71,7 @@ module GraphQL
|
|
70
71
|
association: association,
|
71
72
|
base_model_type: base_model_type,
|
72
73
|
model_type: model_type,
|
73
|
-
object_to_base_model: object_to_model
|
74
|
+
object_to_base_model: object_to_model,
|
74
75
|
})
|
75
76
|
|
76
77
|
graph_type.fields[camel_name.to_s] = GraphQL::Field.define do
|
@@ -79,7 +80,7 @@ module GraphQL
|
|
79
80
|
description options[:description] if options.include?(:description)
|
80
81
|
deprecation_reason options[:deprecation_reason] if options.include?(:deprecation_reason)
|
81
82
|
|
82
|
-
resolve -> (model,
|
83
|
+
resolve -> (model, _args, context) do
|
83
84
|
return nil unless model
|
84
85
|
DefinitionHelpers.load_and_traverse(model, [association], context)
|
85
86
|
end
|
@@ -96,7 +97,7 @@ module GraphQL
|
|
96
97
|
association: association,
|
97
98
|
base_model_type: base_model_type,
|
98
99
|
model_type: model_type,
|
99
|
-
object_to_base_model: object_to_model
|
100
|
+
object_to_base_model: object_to_model,
|
100
101
|
})
|
101
102
|
|
102
103
|
can_use_optimized = reflection.macro == :belongs_to
|
@@ -110,7 +111,7 @@ module GraphQL
|
|
110
111
|
type id_field_type
|
111
112
|
deprecation_reason options[:deprecation_reason] if options.include?(:deprecation_reason)
|
112
113
|
|
113
|
-
resolve -> (model,
|
114
|
+
resolve -> (model, _args, context) do
|
114
115
|
return nil unless model
|
115
116
|
|
116
117
|
if can_use_optimized
|
@@ -130,8 +131,8 @@ module GraphQL
|
|
130
131
|
def self.define_has_many_array(graph_type, base_model_type, model_type, path, association, object_to_model, options, detect_nulls)
|
131
132
|
reflection = model_type.reflect_on_association(association)
|
132
133
|
|
133
|
-
|
134
|
-
|
134
|
+
raise ArgumentError, "Association #{association} wasn't found on model #{model_type.name}" unless reflection
|
135
|
+
raise ArgumentError, "Cannot include #{reflection.macro} association #{association} on model #{model_type.name} with has_many_array" unless [:has_many].include?(reflection.macro)
|
135
136
|
|
136
137
|
association_type = options[:type] || GraphQL::Models.get_graphql_type!(reflection.klass)
|
137
138
|
|
@@ -144,7 +145,7 @@ module GraphQL
|
|
144
145
|
# The has_many associations are a little special. Instead of checking for a presence validator, we instead assume
|
145
146
|
# that the outer type should be non-null, unless detect_nulls is false. In other words, we prefer an empty
|
146
147
|
# array for the association, rather than null.
|
147
|
-
if (options[:nullable]
|
148
|
+
if (options[:nullable].nil? && detect_nulls) || options[:nullable] == false
|
148
149
|
association_type = association_type.to_non_null_type
|
149
150
|
id_field_type = id_field_type.to_non_null_type
|
150
151
|
end
|
@@ -158,7 +159,7 @@ module GraphQL
|
|
158
159
|
association: association,
|
159
160
|
base_model_type: base_model_type,
|
160
161
|
model_type: model_type,
|
161
|
-
object_to_base_model: object_to_model
|
162
|
+
object_to_base_model: object_to_model,
|
162
163
|
})
|
163
164
|
|
164
165
|
graph_type.fields[camel_name.to_s] = GraphQL::Field.define do
|
@@ -167,7 +168,7 @@ module GraphQL
|
|
167
168
|
description options[:description] if options.include?(:description)
|
168
169
|
deprecation_reason options[:deprecation_reason] if options.include?(:deprecation_reason)
|
169
170
|
|
170
|
-
resolve -> (model,
|
171
|
+
resolve -> (model, _args, context) do
|
171
172
|
return nil unless model
|
172
173
|
DefinitionHelpers.load_and_traverse(model, [association], context).then do |result|
|
173
174
|
Array.wrap(result)
|
@@ -185,7 +186,7 @@ module GraphQL
|
|
185
186
|
association: association,
|
186
187
|
base_model_type: base_model_type,
|
187
188
|
model_type: model_type,
|
188
|
-
object_to_base_model: object_to_model
|
189
|
+
object_to_base_model: object_to_model,
|
189
190
|
})
|
190
191
|
|
191
192
|
graph_type.fields[id_field_name.to_s] = GraphQL::Field.define do
|
@@ -193,7 +194,7 @@ module GraphQL
|
|
193
194
|
type id_field_type
|
194
195
|
deprecation_reason options[:deprecation_reason] if options.include?(:deprecation_reason)
|
195
196
|
|
196
|
-
resolve -> (model,
|
197
|
+
resolve -> (model, _args, context) do
|
197
198
|
return nil unless model
|
198
199
|
DefinitionHelpers.load_and_traverse(model, [association], context).then do |result|
|
199
200
|
Array.wrap(result).map(&:gid)
|
@@ -205,12 +206,12 @@ module GraphQL
|
|
205
206
|
def self.define_has_many_connection(graph_type, base_model_type, model_type, path, association, object_to_model, options, detect_nulls)
|
206
207
|
reflection = model_type.reflect_on_association(association)
|
207
208
|
|
208
|
-
|
209
|
-
|
209
|
+
raise ArgumentError, "Association #{association} wasn't found on model #{model_type.name}" unless reflection
|
210
|
+
raise ArgumentError, "Cannot include #{reflection.macro} association #{association} on model #{model_type.name} with has_many_connection" unless [:has_many].include?(reflection.macro)
|
210
211
|
|
211
212
|
connection_type = GraphQL::Models.get_graphql_type!(reflection.klass).connection_type
|
212
213
|
|
213
|
-
if (options[:nullable]
|
214
|
+
if (options[:nullable].nil? && detect_nulls) || options[:nullable] == false
|
214
215
|
connection_type = connection_type.to_non_null_type
|
215
216
|
end
|
216
217
|
|
@@ -223,11 +224,11 @@ module GraphQL
|
|
223
224
|
association: association,
|
224
225
|
base_model_type: base_model_type,
|
225
226
|
model_type: model_type,
|
226
|
-
object_to_base_model: object_to_model
|
227
|
+
object_to_base_model: object_to_model,
|
227
228
|
})
|
228
229
|
|
229
230
|
GraphQL::Define::AssignConnection.call(graph_type, camel_name, connection_type) do
|
230
|
-
resolve -> (model,
|
231
|
+
resolve -> (model, _args, _context) do
|
231
232
|
return nil unless model
|
232
233
|
model.public_send(association)
|
233
234
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module GraphQL
|
2
3
|
module Models
|
3
4
|
module DefinitionHelpers
|
@@ -8,7 +9,7 @@ module GraphQL
|
|
8
9
|
# attribute itself.
|
9
10
|
nullable = options[:nullable]
|
10
11
|
|
11
|
-
if nullable
|
12
|
+
if nullable.nil?
|
12
13
|
nullable = !(detect_nulls && Reflection.is_required(model_class, attribute_or_association))
|
13
14
|
end
|
14
15
|
|
@@ -32,7 +33,7 @@ module GraphQL
|
|
32
33
|
attribute: attribute,
|
33
34
|
base_model_class: base_model_class,
|
34
35
|
model_class: model_class,
|
35
|
-
object_to_base_model: object_to_model
|
36
|
+
object_to_base_model: object_to_model,
|
36
37
|
})
|
37
38
|
|
38
39
|
graph_type.fields[field_name.to_s] = GraphQL::Field.define do
|
@@ -41,7 +42,7 @@ module GraphQL
|
|
41
42
|
description options[:description] if options.include?(:description)
|
42
43
|
deprecation_reason options[:deprecation_reason] if options.include?(:deprecation_reason)
|
43
44
|
|
44
|
-
resolve -> (model,
|
45
|
+
resolve -> (model, _args, _context) do
|
45
46
|
model&.public_send(attribute)
|
46
47
|
end
|
47
48
|
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module GraphQL::Models
|
2
3
|
class MutationFieldMap
|
3
4
|
attr_accessor :model_type, :find_by, :null_behavior, :fields, :nested_maps
|
@@ -6,8 +7,8 @@ module GraphQL::Models
|
|
6
7
|
attr_accessor :name, :association, :has_many, :required, :path
|
7
8
|
|
8
9
|
def initialize(model_type, find_by:, null_behavior:)
|
9
|
-
|
10
|
-
|
10
|
+
raise ArgumentError, "model_type must be a model" if model_type && !(model_type <= ActiveRecord::Base)
|
11
|
+
raise ArgumentError, "null_behavior must be :set_null or :leave_unchanged" unless [:set_null, :leave_unchanged].include?(null_behavior)
|
11
12
|
|
12
13
|
@fields = []
|
13
14
|
@nested_maps = []
|
@@ -27,7 +28,7 @@ module GraphQL::Models
|
|
27
28
|
attribute = attribute.to_sym if attribute.is_a?(String)
|
28
29
|
|
29
30
|
if type.nil? && !model_type
|
30
|
-
|
31
|
+
raise ArgumentError, "You must specify a type for attribute #{name}, because its model type is not known until runtime."
|
31
32
|
end
|
32
33
|
|
33
34
|
if type.nil? && (attribute == :id || foreign_keys.include?(attribute))
|
@@ -51,7 +52,7 @@ module GraphQL::Models
|
|
51
52
|
name: name,
|
52
53
|
attribute: attribute,
|
53
54
|
type: type,
|
54
|
-
required: required
|
55
|
+
required: required,
|
55
56
|
})
|
56
57
|
end
|
57
58
|
|
@@ -62,7 +63,7 @@ module GraphQL::Models
|
|
62
63
|
|
63
64
|
if reflection
|
64
65
|
unless [:belongs_to, :has_one].include?(reflection.macro)
|
65
|
-
|
66
|
+
raise ArgumentError, "Cannot proxy to #{reflection.macro} association #{association} from #{model_type.name}"
|
66
67
|
end
|
67
68
|
|
68
69
|
klass = reflection.polymorphic? ? nil : reflection.klass
|
@@ -83,7 +84,7 @@ module GraphQL::Models
|
|
83
84
|
attribute: field[:attribute],
|
84
85
|
type: field[:type],
|
85
86
|
required: field[:required],
|
86
|
-
path: [association] + Array.wrap(field[:path])
|
87
|
+
path: [association] + Array.wrap(field[:path]),
|
87
88
|
})
|
88
89
|
end
|
89
90
|
|
@@ -95,18 +96,18 @@ module GraphQL::Models
|
|
95
96
|
|
96
97
|
def nested(association, find_by: nil, null_behavior:, name: nil, has_many: false, &block)
|
97
98
|
unless model_type
|
98
|
-
|
99
|
+
raise ArgumentError, "Cannot use `nested` unless the model type is known at build time."
|
99
100
|
end
|
100
101
|
|
101
102
|
association = association.to_sym if association.is_a?(String)
|
102
103
|
reflection = model_type.reflect_on_association(association)
|
103
104
|
|
104
105
|
unless reflection
|
105
|
-
|
106
|
+
raise ArgumentError, "Could not find association #{association} on #{model_type.name}"
|
106
107
|
end
|
107
108
|
|
108
109
|
if reflection.polymorphic?
|
109
|
-
|
110
|
+
raise ArgumentError, "Cannot used `nested` with polymorphic association #{association} on #{model_type.name}"
|
110
111
|
end
|
111
112
|
|
112
113
|
has_many = reflection.macro == :has_many
|
@@ -133,7 +134,7 @@ module GraphQL::Models
|
|
133
134
|
|
134
135
|
def detect_field_conflict(name)
|
135
136
|
if fields.any? { |f| f[name] == name } || nested_maps.any? { |n| n.name == name }
|
136
|
-
|
137
|
+
raise ArgumentError, "The field #{name} is defined more than once."
|
137
138
|
end
|
138
139
|
end
|
139
140
|
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module GraphQL::Models
|
2
3
|
module MutationHelpers
|
3
4
|
def self.apply_changes(field_map, model, inputs, context)
|
@@ -40,7 +41,7 @@ module GraphQL::Models
|
|
40
41
|
end
|
41
42
|
end
|
42
43
|
|
43
|
-
|
44
|
+
changes
|
44
45
|
end
|
45
46
|
|
46
47
|
def self.handle_nested_map(parent_map, parent_model, inputs, context, child_map)
|
@@ -66,7 +67,7 @@ module GraphQL::Models
|
|
66
67
|
changes.concat(child_changes)
|
67
68
|
end
|
68
69
|
|
69
|
-
|
70
|
+
changes
|
70
71
|
end
|
71
72
|
|
72
73
|
def self.match_inputs_to_models(model, child_map, next_inputs, changes)
|
@@ -92,7 +93,7 @@ module GraphQL::Models
|
|
92
93
|
changes.push({ model_instance: child_model, action: :create })
|
93
94
|
end
|
94
95
|
|
95
|
-
|
96
|
+
[{ child_model: child_model, child_inputs: next_inputs }]
|
96
97
|
else
|
97
98
|
next_inputs = [] if next_inputs.nil?
|
98
99
|
|
@@ -108,7 +109,7 @@ module GraphQL::Models
|
|
108
109
|
end
|
109
110
|
end
|
110
111
|
|
111
|
-
def self.match_inputs_by_position(
|
112
|
+
def self.match_inputs_by_position(_model, _child_map, next_inputs, changes, associated_models)
|
112
113
|
count = [associated_models.length, next_inputs.length].max
|
113
114
|
|
114
115
|
matches = []
|
@@ -130,10 +131,10 @@ module GraphQL::Models
|
|
130
131
|
matches.push({ child_model: child_model, child_inputs: inputs, input_path: idx })
|
131
132
|
end
|
132
133
|
|
133
|
-
|
134
|
+
matches
|
134
135
|
end
|
135
136
|
|
136
|
-
def self.match_inputs_by_fields(
|
137
|
+
def self.match_inputs_by_fields(_model, child_map, next_inputs, changes, associated_models, find_by)
|
137
138
|
# Convert the find_by into the field definitions, so that we properly unmap aliased fields
|
138
139
|
find_by_defs = find_by.map { |name| child_map.fields.detect { |f| f[:attribute].to_s == name.to_s } }
|
139
140
|
name_to_attr = find_by_defs.map { |f| [f[:name], f[:attribute].to_s] }.to_h
|
@@ -175,7 +176,7 @@ module GraphQL::Models
|
|
175
176
|
matches.push({ child_model: child_model, child_inputs: inputs, input_path: next_inputs.index(inputs) })
|
176
177
|
end
|
177
178
|
|
178
|
-
|
179
|
+
matches
|
179
180
|
end
|
180
181
|
|
181
182
|
# Returns the instance of the model that will be changed for this field. If new models are created along the way,
|
@@ -197,7 +198,7 @@ module GraphQL::Models
|
|
197
198
|
model_to_change = next_model
|
198
199
|
end
|
199
200
|
|
200
|
-
|
201
|
+
model_to_change
|
201
202
|
end
|
202
203
|
|
203
204
|
def self.apply_field_value(model, field_def, value, context, changes)
|
@@ -206,7 +207,7 @@ module GraphQL::Models
|
|
206
207
|
target_model = GraphQL::Models.model_from_id.call(value, context)
|
207
208
|
|
208
209
|
unless target_model
|
209
|
-
|
210
|
+
raise GraphQL::ExecutionError, "The value provided for #{field_def[:name]} does not refer to a valid model."
|
210
211
|
end
|
211
212
|
|
212
213
|
value = target_model.id
|
@@ -219,7 +220,7 @@ module GraphQL::Models
|
|
219
220
|
model_instance: model,
|
220
221
|
input_path: field_def[:name],
|
221
222
|
attribute: field_def[:attribute],
|
222
|
-
action: model.new_record? ? :create : :update
|
223
|
+
action: model.new_record? ? :create : :update,
|
223
224
|
})
|
224
225
|
end
|
225
226
|
end
|
@@ -250,6 +251,5 @@ module GraphQL::Models
|
|
250
251
|
|
251
252
|
values
|
252
253
|
end
|
253
|
-
|
254
254
|
end
|
255
255
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module GraphQL::Models
|
2
3
|
module MutationHelpers
|
3
4
|
def self.print_input_fields(field_map, definer, map_name_prefix)
|
@@ -14,8 +15,8 @@ module GraphQL::Models
|
|
14
15
|
|
15
16
|
if field_map.leave_null_unchanged?
|
16
17
|
field_names = field_map.fields.select { |f| !f[:required] }.map { |f| f[:name] }
|
17
|
-
field_names += field_map.nested_maps.reject(&:required).map
|
18
|
-
field_names = field_names.
|
18
|
+
field_names += field_map.nested_maps.reject(&:required).map(&:name)
|
19
|
+
field_names = field_names.sort
|
19
20
|
|
20
21
|
unless field_names.empty?
|
21
22
|
enum = GraphQL::EnumType.define do
|
@@ -1,12 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module GraphQL::Models
|
2
3
|
module MutationHelpers
|
3
|
-
def self.validate_changes(inputs, field_map, root_model,
|
4
|
+
def self.validate_changes(inputs, field_map, root_model, _context, all_changes)
|
4
5
|
invalid_fields = {}
|
5
6
|
unknown_errors = []
|
6
7
|
|
7
8
|
changed_models = all_changes.group_by { |c| c[:model_instance] }
|
8
9
|
|
9
|
-
changed_models.reject { |m,
|
10
|
+
changed_models.reject { |m, _v| m.valid? }.each do |model, changes|
|
10
11
|
attrs_to_field = changes
|
11
12
|
.select { |c| c[:attribute] && c[:input_path] }
|
12
13
|
.map { |c| [c[:attribute], c[:input_path]] }
|
@@ -29,7 +30,7 @@ module GraphQL::Models
|
|
29
30
|
modelType: model.class.name,
|
30
31
|
modelRid: model.id,
|
31
32
|
attribute: attribute,
|
32
|
-
message: message
|
33
|
+
message: message,
|
33
34
|
})
|
34
35
|
end
|
35
36
|
end
|
@@ -37,11 +38,11 @@ module GraphQL::Models
|
|
37
38
|
end
|
38
39
|
|
39
40
|
unless invalid_fields.empty? && unknown_errors.empty?
|
40
|
-
|
41
|
+
raise ValidationError.new(invalid_fields, unknown_errors)
|
41
42
|
end
|
42
43
|
end
|
43
44
|
|
44
|
-
def self.add_error(
|
45
|
+
def self.add_error(_attribute, message, path, invalid_fields)
|
45
46
|
path = Array.wrap(path)
|
46
47
|
|
47
48
|
current = invalid_fields
|
@@ -96,9 +97,7 @@ module GraphQL::Models
|
|
96
97
|
end
|
97
98
|
end
|
98
99
|
|
99
|
-
|
100
|
+
nil
|
100
101
|
end
|
101
|
-
|
102
|
-
|
103
102
|
end
|
104
103
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module GraphQL::Models
|
2
3
|
module MutationHelpers
|
3
4
|
class ValidationError < GraphQL::ExecutionError
|
@@ -13,16 +14,14 @@ module GraphQL::Models
|
|
13
14
|
'message' => "Some of your changes could not be saved.",
|
14
15
|
'kind' => "INVALID_ARGUMENTS",
|
15
16
|
'invalidArguments' => invalid_arguments,
|
16
|
-
'unknownErrors' => unknown_errors
|
17
|
+
'unknownErrors' => unknown_errors,
|
17
18
|
}
|
18
19
|
|
19
20
|
if ast_node
|
20
|
-
values
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
}]
|
25
|
-
})
|
21
|
+
values['locations'] = [{
|
22
|
+
"line" => ast_node.line,
|
23
|
+
"column" => ast_node.col,
|
24
|
+
},]
|
26
25
|
end
|
27
26
|
|
28
27
|
values
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module GraphQL::Models
|
2
3
|
class Mutator
|
3
4
|
attr_accessor :field_map, :root_model, :inputs, :context
|
@@ -10,28 +11,28 @@ module GraphQL::Models
|
|
10
11
|
end
|
11
12
|
|
12
13
|
def apply_changes
|
13
|
-
|
14
|
+
raise StandardError, "Called apply_changes twice for the same mutator" if @all_changes
|
14
15
|
@all_changes = MutationHelpers.apply_changes(field_map, root_model, inputs, context)
|
15
16
|
changed_models
|
16
17
|
end
|
17
18
|
|
18
19
|
def changed_models
|
19
|
-
|
20
|
+
raise StandardError, "Need to call apply_changes before #{__method__}" unless @all_changes
|
20
21
|
@all_changes.map { |c| c[:model_instance] }.uniq
|
21
22
|
end
|
22
23
|
|
23
24
|
def validate!
|
24
|
-
|
25
|
+
raise StandardError, "Need to call apply_changes before #{__method__}" unless @all_changes
|
25
26
|
MutationHelpers.validate_changes(inputs, field_map, root_model, context, @all_changes)
|
26
27
|
end
|
27
28
|
|
28
29
|
def authorize!
|
29
|
-
|
30
|
+
raise StandardError, "Need to call apply_changes before #{__method__}" unless @all_changes
|
30
31
|
MutationHelpers.authorize_changes(context, @all_changes)
|
31
32
|
end
|
32
33
|
|
33
34
|
def save!
|
34
|
-
|
35
|
+
raise StandardError, "Need to call apply_changes before #{__method__}" unless @all_changes
|
35
36
|
|
36
37
|
ActiveRecord::Base.transaction(requires_new: true) do
|
37
38
|
changed_models.each do |model|
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
# Exposes utility methods for getting metadata out of active record models
|
2
3
|
module GraphQL::Models
|
3
4
|
module Reflection
|
@@ -10,7 +11,7 @@ module GraphQL::Models
|
|
10
11
|
# Ignore any inclusion validators that are using the 'if' or 'unless' options
|
11
12
|
validators = validators.reject { |v| v.options.include?(:if) || v.options.include?(:unless) || v.options[:in].blank? }
|
12
13
|
return nil unless validators.any?
|
13
|
-
|
14
|
+
validators.map { |v| v.options[:in] }.reduce(:&)
|
14
15
|
end
|
15
16
|
|
16
17
|
# Determines if the attribute (or association) is required by examining presence validators
|
@@ -43,13 +44,13 @@ module GraphQL::Models
|
|
43
44
|
active_record_type = model_class.type_for_attribute(attribute.to_s)
|
44
45
|
|
45
46
|
if active_record_type.type.nil?
|
46
|
-
|
47
|
+
raise ArgumentError, "The type for attribute #{attribute} wasn't found on #{model_class.name}"
|
47
48
|
end
|
48
49
|
|
49
50
|
result = DatabaseTypes.registered_type(active_record_type.type)
|
50
51
|
|
51
52
|
if !result
|
52
|
-
|
53
|
+
raise "The type #{active_record_type} is not registered with DatabaseTypes (attribute #{attribute} on #{model_class.name})"
|
53
54
|
end
|
54
55
|
|
55
56
|
# Arrays: Rails doesn't have a generalized way to detect arrays, so we use this method to do it:
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module GraphQL
|
2
3
|
module Models
|
3
4
|
class RelationLoadRequest
|
@@ -26,8 +27,7 @@ module GraphQL
|
|
26
27
|
end
|
27
28
|
|
28
29
|
# When the request is fulfilled, this method is called so that it can do whatever caching, etc. is needed
|
29
|
-
def fulfilled(result)
|
30
|
-
end
|
30
|
+
def fulfilled(result); end
|
31
31
|
|
32
32
|
def load
|
33
33
|
loader.load(self)
|
@@ -46,7 +46,6 @@ module GraphQL
|
|
46
46
|
def loader
|
47
47
|
@loader ||= RelationLoader.for(target_class)
|
48
48
|
end
|
49
|
-
|
50
49
|
end
|
51
50
|
end
|
52
51
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module GraphQL
|
2
3
|
module Models
|
3
4
|
class RelationLoader < GraphQL::Batch::Loader
|
@@ -8,7 +9,6 @@ module GraphQL
|
|
8
9
|
end
|
9
10
|
|
10
11
|
def perform(load_requests)
|
11
|
-
|
12
12
|
# Group the requests to load by id into a single relation, and we'll fan it back out after
|
13
13
|
# we have the results
|
14
14
|
|
@@ -38,7 +38,7 @@ module GraphQL
|
|
38
38
|
end
|
39
39
|
|
40
40
|
# Generate a CASE column that will tell us whether the row matches this particular relation
|
41
|
-
slicing_columns = relations.each_with_index.map do |
|
41
|
+
slicing_columns = relations.each_with_index.map do |_relation, index|
|
42
42
|
%{ CASE WHEN "#{model_class.table_name}"."#{model_class.primary_key}" IN (#{selection_clauses[index]}) THEN 1 ELSE 0 END AS "in_relation_#{index}" }
|
43
43
|
end
|
44
44
|
|
@@ -64,7 +64,7 @@ module GraphQL
|
|
64
64
|
# Build the query that will select any of the rows that match the selection clauses
|
65
65
|
main_relation = model_class
|
66
66
|
.where("id in ((#{selection_clauses.join(") UNION (")}))")
|
67
|
-
.select(%
|
67
|
+
.select(%( "#{model_class.table_name}".* ))
|
68
68
|
|
69
69
|
main_relation = slicing_columns.reduce(main_relation) { |relation, memo| relation.select(memo) }
|
70
70
|
main_relation = sorting_columns.reduce(main_relation) { |relation, memo| relation.select(memo) }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql-activerecord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Foster
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-04-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -140,6 +140,20 @@ dependencies:
|
|
140
140
|
- - ">="
|
141
141
|
- !ruby/object:Gem::Version
|
142
142
|
version: '0'
|
143
|
+
- !ruby/object:Gem::Dependency
|
144
|
+
name: rubocop
|
145
|
+
requirement: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - "~>"
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: 0.47.1
|
150
|
+
type: :development
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
requirements:
|
154
|
+
- - "~>"
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
version: 0.47.1
|
143
157
|
description: Build Relay-compatible GraphQL schemas based on ActiveRecord models
|
144
158
|
email:
|
145
159
|
- theorygeek@gmail.com
|
@@ -148,8 +162,11 @@ extensions: []
|
|
148
162
|
extra_rdoc_files: []
|
149
163
|
files:
|
150
164
|
- ".bundle/config"
|
165
|
+
- ".circleci/config.yml"
|
151
166
|
- ".gitignore"
|
152
167
|
- ".rspec"
|
168
|
+
- ".rubocop.yml"
|
169
|
+
- ".rubocop_todo.yml"
|
153
170
|
- ".travis.yml"
|
154
171
|
- CHANGELOG.md
|
155
172
|
- CODE_OF_CONDUCT.md
|
@@ -168,6 +185,9 @@ files:
|
|
168
185
|
- bin/pry
|
169
186
|
- bin/rake
|
170
187
|
- bin/rspec
|
188
|
+
- bin/rubocop
|
189
|
+
- bin/ruby-parse
|
190
|
+
- bin/ruby-rewrite
|
171
191
|
- bin/setup
|
172
192
|
- bin/thor
|
173
193
|
- graphql-activerecord.gemspec
|