fragment-dev 1.4.6 → 2.1.0
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/fragment.schema.json +1077 -101
- data/lib/fragment_client/graphql_ast.rb +73 -0
- data/lib/fragment_client/typed_entries.rb +444 -0
- data/lib/fragment_client/typed_ledger_entry.rb +281 -0
- data/lib/fragment_client/version.rb +1 -1
- data/lib/fragment_client.rb +169 -16
- data/lib/queries.graphql +101 -75
- data/lib/tapioca/dsl/compilers/fragment_query_methods.rb +61 -0
- data/lib/tapioca/dsl/compilers/fragment_response_types.rb +222 -0
- data/lib/tapioca/dsl/compilers/fragment_typed_entries.rb +169 -0
- data/lib/tapioca/dsl/helpers/graphql_sorbet_types.rb +53 -0
- metadata +9 -2
data/lib/queries.graphql
CHANGED
|
@@ -24,9 +24,7 @@ mutation StoreSchema($schema: SchemaInput!) {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
mutation DeleteSchema(
|
|
28
|
-
$schema: SchemaMatchInput!
|
|
29
|
-
) {
|
|
27
|
+
mutation DeleteSchema($schema: SchemaMatchInput!) {
|
|
30
28
|
deleteSchema(schema: $schema) {
|
|
31
29
|
__typename
|
|
32
30
|
... on DeleteSchemaResult {
|
|
@@ -77,9 +75,7 @@ mutation CreateLedger(
|
|
|
77
75
|
}
|
|
78
76
|
}
|
|
79
77
|
|
|
80
|
-
mutation DeleteLedger(
|
|
81
|
-
$ledger: LedgerMatchInput!
|
|
82
|
-
) {
|
|
78
|
+
mutation DeleteLedger($ledger: LedgerMatchInput!) {
|
|
83
79
|
deleteLedger(ledger: $ledger) {
|
|
84
80
|
__typename
|
|
85
81
|
... on DeleteLedgerResult {
|
|
@@ -98,6 +94,52 @@ mutation DeleteLedger(
|
|
|
98
94
|
}
|
|
99
95
|
}
|
|
100
96
|
|
|
97
|
+
mutation AddLedgerEntries($entries: [AddLedgerEntryInput!]!) {
|
|
98
|
+
addLedgerEntries(entries: $entries) {
|
|
99
|
+
__typename
|
|
100
|
+
... on AddLedgerEntriesResult {
|
|
101
|
+
results {
|
|
102
|
+
isIkReplay
|
|
103
|
+
entry {
|
|
104
|
+
type
|
|
105
|
+
id
|
|
106
|
+
ik
|
|
107
|
+
posted
|
|
108
|
+
created
|
|
109
|
+
}
|
|
110
|
+
lines {
|
|
111
|
+
id
|
|
112
|
+
amount
|
|
113
|
+
account {
|
|
114
|
+
path
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
... on AddLedgerEntriesError {
|
|
120
|
+
code
|
|
121
|
+
message
|
|
122
|
+
retryable
|
|
123
|
+
errors {
|
|
124
|
+
ik
|
|
125
|
+
code
|
|
126
|
+
message
|
|
127
|
+
retryable
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
... on BadRequestError {
|
|
131
|
+
code
|
|
132
|
+
message
|
|
133
|
+
retryable
|
|
134
|
+
}
|
|
135
|
+
... on InternalError {
|
|
136
|
+
code
|
|
137
|
+
message
|
|
138
|
+
retryable
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
101
143
|
mutation AddLedgerEntry(
|
|
102
144
|
$ik: SafeString!
|
|
103
145
|
$ledgerIk: SafeString!
|
|
@@ -151,9 +193,7 @@ mutation AddLedgerEntry(
|
|
|
151
193
|
}
|
|
152
194
|
}
|
|
153
195
|
|
|
154
|
-
mutation ReverseLedgerEntry(
|
|
155
|
-
$id: ID!
|
|
156
|
-
) {
|
|
196
|
+
mutation ReverseLedgerEntry($id: ID!) {
|
|
157
197
|
reverseLedgerEntry(id: $id) {
|
|
158
198
|
__typename
|
|
159
199
|
... on ReverseLedgerEntryResult {
|
|
@@ -208,14 +248,8 @@ mutation ReverseLedgerEntry(
|
|
|
208
248
|
}
|
|
209
249
|
}
|
|
210
250
|
|
|
211
|
-
mutation MigrateLedgerEntry(
|
|
212
|
-
$id:
|
|
213
|
-
$newLedgerEntry: LedgerEntryInput!
|
|
214
|
-
) {
|
|
215
|
-
migrateLedgerEntry(input: {
|
|
216
|
-
id: $id
|
|
217
|
-
newLedgerEntry: $newLedgerEntry
|
|
218
|
-
}) {
|
|
251
|
+
mutation MigrateLedgerEntry($id: ID!, $newLedgerEntry: LedgerEntryInput!) {
|
|
252
|
+
migrateLedgerEntry(input: { id: $id, newLedgerEntry: $newLedgerEntry }) {
|
|
219
253
|
__typename
|
|
220
254
|
... on MigrateLedgerEntryResult {
|
|
221
255
|
reversingLedgerEntry {
|
|
@@ -689,6 +723,8 @@ query ListLedgerAccountBalances(
|
|
|
689
723
|
$balanceCurrency: CurrencyMatchInput
|
|
690
724
|
$balanceAt: LastMoment
|
|
691
725
|
$ownBalanceConsistencyMode: ReadBalanceConsistencyMode
|
|
726
|
+
$childBalanceConsistencyMode: ReadBalanceConsistencyMode
|
|
727
|
+
$balanceConsistencyMode: ReadBalanceConsistencyMode
|
|
692
728
|
) {
|
|
693
729
|
ledger(ledger: { ik: $ledgerIk }) {
|
|
694
730
|
id
|
|
@@ -707,8 +743,16 @@ query ListLedgerAccountBalances(
|
|
|
707
743
|
at: $balanceAt
|
|
708
744
|
consistencyMode: $ownBalanceConsistencyMode
|
|
709
745
|
)
|
|
710
|
-
childBalance(
|
|
711
|
-
|
|
746
|
+
childBalance(
|
|
747
|
+
currency: $balanceCurrency
|
|
748
|
+
at: $balanceAt
|
|
749
|
+
consistencyMode: $childBalanceConsistencyMode
|
|
750
|
+
)
|
|
751
|
+
balance(
|
|
752
|
+
currency: $balanceCurrency
|
|
753
|
+
at: $balanceAt
|
|
754
|
+
consistencyMode: $balanceConsistencyMode
|
|
755
|
+
)
|
|
712
756
|
}
|
|
713
757
|
pageInfo {
|
|
714
758
|
hasNextPage
|
|
@@ -727,6 +771,8 @@ query ListMultiCurrencyLedgerAccountBalances(
|
|
|
727
771
|
$before: String
|
|
728
772
|
$balanceAt: LastMoment
|
|
729
773
|
$ownBalancesConsistencyMode: ReadBalanceConsistencyMode
|
|
774
|
+
$childBalancesConsistencyMode: ReadBalanceConsistencyMode
|
|
775
|
+
$balancesConsistencyMode: ReadBalanceConsistencyMode
|
|
730
776
|
) {
|
|
731
777
|
ledger(ledger: { ik: $ledgerIk }) {
|
|
732
778
|
id
|
|
@@ -752,7 +798,10 @@ query ListMultiCurrencyLedgerAccountBalances(
|
|
|
752
798
|
amount
|
|
753
799
|
}
|
|
754
800
|
}
|
|
755
|
-
childBalances(
|
|
801
|
+
childBalances(
|
|
802
|
+
at: $balanceAt
|
|
803
|
+
consistencyMode: $childBalancesConsistencyMode
|
|
804
|
+
) {
|
|
756
805
|
nodes {
|
|
757
806
|
currency {
|
|
758
807
|
code
|
|
@@ -761,7 +810,7 @@ query ListMultiCurrencyLedgerAccountBalances(
|
|
|
761
810
|
amount
|
|
762
811
|
}
|
|
763
812
|
}
|
|
764
|
-
balances(at: $balanceAt) {
|
|
813
|
+
balances(at: $balanceAt, consistencyMode: $balancesConsistencyMode) {
|
|
765
814
|
nodes {
|
|
766
815
|
currency {
|
|
767
816
|
code
|
|
@@ -815,24 +864,7 @@ query GetLedgerAccountBalance(
|
|
|
815
864
|
$ledgerIk: SafeString!
|
|
816
865
|
$balanceCurrency: CurrencyMatchInput
|
|
817
866
|
$balanceAt: LastMoment
|
|
818
|
-
$
|
|
819
|
-
) {
|
|
820
|
-
ledgerAccount(ledgerAccount: { ledger: { ik: $ledgerIk }, path: $path }) {
|
|
821
|
-
id
|
|
822
|
-
path
|
|
823
|
-
ownBalance(
|
|
824
|
-
currency: $balanceCurrency
|
|
825
|
-
at: $balanceAt
|
|
826
|
-
consistencyMode: $ownBalanceConsistencyMode
|
|
827
|
-
)
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
query GetLedgerAccountBalanceWithChildRollup(
|
|
832
|
-
$path: String!
|
|
833
|
-
$ledgerIk: SafeString!
|
|
834
|
-
$balanceCurrency: CurrencyMatchInput
|
|
835
|
-
$balanceAt: LastMoment
|
|
867
|
+
$balanceConsistencyMode: ReadBalanceConsistencyMode
|
|
836
868
|
) {
|
|
837
869
|
ledgerAccount(ledgerAccount: { ledger: { ik: $ledgerIk }, path: $path }) {
|
|
838
870
|
id
|
|
@@ -840,11 +872,11 @@ query GetLedgerAccountBalanceWithChildRollup(
|
|
|
840
872
|
balance(
|
|
841
873
|
currency: $balanceCurrency
|
|
842
874
|
at: $balanceAt
|
|
875
|
+
consistencyMode: $balanceConsistencyMode
|
|
843
876
|
)
|
|
844
877
|
}
|
|
845
878
|
}
|
|
846
879
|
|
|
847
|
-
|
|
848
880
|
query GetSchema($key: SafeString!, $version: Int) {
|
|
849
881
|
schema(schema: { key: $key, version: $version }) {
|
|
850
882
|
key
|
|
@@ -912,21 +944,23 @@ query ListLedgerEntryGroupBalances(
|
|
|
912
944
|
$last: Int
|
|
913
945
|
$filter: LedgerEntryGroupBalanceFilterSet
|
|
914
946
|
) {
|
|
915
|
-
ledgerEntryGroup(
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
947
|
+
ledgerEntryGroup(
|
|
948
|
+
ledgerEntryGroup: {
|
|
949
|
+
ledger: { ik: $ledgerIk }
|
|
950
|
+
key: $groupKey
|
|
951
|
+
value: $groupValue
|
|
952
|
+
}
|
|
953
|
+
) {
|
|
920
954
|
key
|
|
921
955
|
value
|
|
922
956
|
created
|
|
923
957
|
balances(
|
|
924
|
-
after: $after
|
|
925
|
-
before: $before
|
|
926
|
-
first: $first
|
|
927
|
-
last: $last
|
|
928
|
-
filter: $filter
|
|
929
|
-
){
|
|
958
|
+
after: $after
|
|
959
|
+
before: $before
|
|
960
|
+
first: $first
|
|
961
|
+
last: $last
|
|
962
|
+
filter: $filter
|
|
963
|
+
) {
|
|
930
964
|
nodes {
|
|
931
965
|
account {
|
|
932
966
|
path
|
|
@@ -1020,12 +1054,8 @@ query GetEntriesToMigrateForLedgerEntryDataMigration(
|
|
|
1020
1054
|
ledger(ledger: { ik: $ledgerIk }) {
|
|
1021
1055
|
ledgerEntryDataMigrations(
|
|
1022
1056
|
filter: {
|
|
1023
|
-
entryType: {
|
|
1024
|
-
|
|
1025
|
-
}
|
|
1026
|
-
typeVersion: {
|
|
1027
|
-
equalTo: $typeVersion
|
|
1028
|
-
}
|
|
1057
|
+
entryType: { equalTo: $entryType }
|
|
1058
|
+
typeVersion: { equalTo: $typeVersion }
|
|
1029
1059
|
}
|
|
1030
1060
|
) {
|
|
1031
1061
|
nodes {
|
|
@@ -1049,7 +1079,7 @@ query GetEntriesToMigrateForLedgerEntryDataMigration(
|
|
|
1049
1079
|
id
|
|
1050
1080
|
amount
|
|
1051
1081
|
account {
|
|
1052
|
-
|
|
1082
|
+
path
|
|
1053
1083
|
}
|
|
1054
1084
|
}
|
|
1055
1085
|
}
|
|
@@ -1136,18 +1166,14 @@ query GetEntriesToMigrateForLedgerAccountDataMigration(
|
|
|
1136
1166
|
) {
|
|
1137
1167
|
ledger(ledger: { ik: $ledgerIk }) {
|
|
1138
1168
|
ledgerAccountDataMigrations(
|
|
1139
|
-
filter: {
|
|
1140
|
-
accountPath: {
|
|
1141
|
-
equalTo: $accountPath
|
|
1142
|
-
}
|
|
1143
|
-
}
|
|
1169
|
+
filter: { accountPath: { equalTo: $accountPath } }
|
|
1144
1170
|
) {
|
|
1145
1171
|
nodes {
|
|
1146
1172
|
ledgerEntries(
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1173
|
+
first: $first
|
|
1174
|
+
after: $after
|
|
1175
|
+
last: $last
|
|
1176
|
+
before: $before
|
|
1151
1177
|
) {
|
|
1152
1178
|
nodes {
|
|
1153
1179
|
id
|
|
@@ -1181,17 +1207,17 @@ query GetEntriesToMigrateForLedgerAccountDataMigration(
|
|
|
1181
1207
|
}
|
|
1182
1208
|
|
|
1183
1209
|
mutation CreateCustomCurrency(
|
|
1184
|
-
$id: SafeString
|
|
1185
|
-
$name: String
|
|
1186
|
-
$precision: Int
|
|
1187
|
-
$customCode: String
|
|
1210
|
+
$id: SafeString!
|
|
1211
|
+
$name: String!
|
|
1212
|
+
$precision: Int!
|
|
1213
|
+
$customCode: String!
|
|
1188
1214
|
) {
|
|
1189
1215
|
createCustomCurrency(
|
|
1190
1216
|
customCurrency: {
|
|
1191
|
-
customCurrencyId: $id
|
|
1192
|
-
name: $name
|
|
1193
|
-
precision: $precision
|
|
1194
|
-
customCode: $customCode
|
|
1217
|
+
customCurrencyId: $id
|
|
1218
|
+
name: $name
|
|
1219
|
+
precision: $precision
|
|
1220
|
+
customCode: $customCode
|
|
1195
1221
|
}
|
|
1196
1222
|
) {
|
|
1197
1223
|
... on CreateCustomCurrencyResult {
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
return unless defined?(Tapioca::Dsl::Compilers)
|
|
5
|
+
|
|
6
|
+
require 'fragment_client'
|
|
7
|
+
|
|
8
|
+
module Tapioca
|
|
9
|
+
module Dsl
|
|
10
|
+
module Compilers
|
|
11
|
+
# Declares the query and mutation methods {FragmentClient} answers to.
|
|
12
|
+
#
|
|
13
|
+
# `define_method_from_queries` creates one singleton method per operation on
|
|
14
|
+
# each client instance, so without this Sorbet reports `Method
|
|
15
|
+
# 'create_ledger' does not exist` for every one of them and a consumer has to
|
|
16
|
+
# write `T.unsafe(client)`.
|
|
17
|
+
#
|
|
18
|
+
# Covers the operations shipped in `queries.graphql`, plus any document
|
|
19
|
+
# passed to `FragmentClient.load_queries`. Operations in
|
|
20
|
+
# {FragmentClient::WRAPPED_OPERATIONS} are skipped: those have hand-written
|
|
21
|
+
# methods with real signatures already.
|
|
22
|
+
#
|
|
23
|
+
# `variables` is the operation's variables hash and stays untyped. Its keys
|
|
24
|
+
# are the GraphQL variable names verbatim, as the rest of this SDK passes
|
|
25
|
+
# them. The return type comes from `FragmentResponseTypes`.
|
|
26
|
+
class FragmentQueryMethods < Compiler
|
|
27
|
+
extend T::Sig
|
|
28
|
+
|
|
29
|
+
ConstantType = type_member { { fixed: T.class_of(FragmentClient) } }
|
|
30
|
+
|
|
31
|
+
class << self
|
|
32
|
+
extend T::Sig
|
|
33
|
+
|
|
34
|
+
sig { override.returns(T::Enumerable[Module]) }
|
|
35
|
+
def gather_constants
|
|
36
|
+
[FragmentClient]
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
sig { override.void }
|
|
41
|
+
def decorate
|
|
42
|
+
methods = FragmentGraphQl.operations.keys.to_h do |operation|
|
|
43
|
+
[FragmentGraphQl.method_name_for(operation), operation]
|
|
44
|
+
end
|
|
45
|
+
methods.reject! { |name, _| FragmentClient::WRAPPED_OPERATIONS.include?(name) }
|
|
46
|
+
return if methods.empty?
|
|
47
|
+
|
|
48
|
+
root.create_path(constant) do |klass|
|
|
49
|
+
methods.keys.sort.each do |name|
|
|
50
|
+
klass.create_method(
|
|
51
|
+
name,
|
|
52
|
+
parameters: [create_param('variables', type: 'T::Hash[Symbol, T.untyped]')],
|
|
53
|
+
return_type: "::FragmentClient::Responses::#{methods.fetch(name)}"
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
return unless defined?(Tapioca::Dsl::Compilers)
|
|
5
|
+
|
|
6
|
+
require 'fragment_client'
|
|
7
|
+
require 'tapioca/dsl/helpers/graphql_sorbet_types'
|
|
8
|
+
|
|
9
|
+
module Tapioca
|
|
10
|
+
module Dsl
|
|
11
|
+
module Compilers
|
|
12
|
+
# Declares the response objects the query methods return, so that
|
|
13
|
+
# `client.get_ledger(...).data.ledger.name` typechecks.
|
|
14
|
+
#
|
|
15
|
+
# graphql-client builds those objects at runtime from anonymous classes whose
|
|
16
|
+
# fields are served by `method_missing`, so there is nothing to reflect on.
|
|
17
|
+
# The shape is instead derived statically from each operation's selection set
|
|
18
|
+
# and the pinned Schema -- the same two inputs graphql-client itself uses,
|
|
19
|
+
# which is what makes the declared types agree with the runtime.
|
|
20
|
+
#
|
|
21
|
+
# In particular graphql-client refuses at runtime to read a field the
|
|
22
|
+
# operation did not select (`ImplicitlyFetchedFieldError`), so a type derived
|
|
23
|
+
# from the selection set cannot promise more than the runtime allows.
|
|
24
|
+
#
|
|
25
|
+
# Emitted under `FragmentClient::Responses::<Operation>`, one nested class per
|
|
26
|
+
# selection level.
|
|
27
|
+
class FragmentResponseTypes < Compiler
|
|
28
|
+
extend T::Sig
|
|
29
|
+
|
|
30
|
+
ConstantType = type_member { { fixed: T.class_of(FragmentClient) } }
|
|
31
|
+
|
|
32
|
+
# Scalar names this Schema uses, mapped for `GraphqlSorbetTypes`.
|
|
33
|
+
SCALARS = T.let(
|
|
34
|
+
{
|
|
35
|
+
'String' => '::String', 'ID' => '::String', 'SafeString' => '::String',
|
|
36
|
+
'ParameterizedString' => '::String', 'Date' => '::String',
|
|
37
|
+
'DateTime' => '::String', 'FirstMoment' => '::String',
|
|
38
|
+
'LastMoment' => '::String', 'Period' => '::String',
|
|
39
|
+
'PeriodFilter' => '::String', 'UTCOffset' => '::String',
|
|
40
|
+
'Int96' => '::String', 'Int' => '::Integer', 'Float' => '::Float',
|
|
41
|
+
'Boolean' => 'T::Boolean', 'JSON' => 'T.untyped', 'Parameters' => 'T.untyped'
|
|
42
|
+
}.freeze,
|
|
43
|
+
T::Hash[String, String]
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
RESPONSES_NAMESPACE = 'FragmentClient::Responses'
|
|
47
|
+
|
|
48
|
+
class << self
|
|
49
|
+
extend T::Sig
|
|
50
|
+
|
|
51
|
+
sig { override.returns(T::Enumerable[Module]) }
|
|
52
|
+
def gather_constants
|
|
53
|
+
[FragmentClient]
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
sig { override.void }
|
|
58
|
+
def decorate
|
|
59
|
+
operations = FragmentGraphQl.operations
|
|
60
|
+
return if operations.empty?
|
|
61
|
+
|
|
62
|
+
root.create_module(RESPONSES_NAMESPACE) do |namespace|
|
|
63
|
+
operations.keys.sort.each do |name|
|
|
64
|
+
declare_operation(namespace, name, T.must(operations[name]))
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
sig do
|
|
72
|
+
params(namespace: RBI::Scope, name: String,
|
|
73
|
+
operation: GraphQL::Language::Nodes::OperationDefinition).void
|
|
74
|
+
end
|
|
75
|
+
def declare_operation(namespace, name, operation)
|
|
76
|
+
root_type = operation.operation_type == 'mutation' ? schema.mutation : schema.query
|
|
77
|
+
return if root_type.nil?
|
|
78
|
+
|
|
79
|
+
namespace.create_class(name) do |response|
|
|
80
|
+
response.create_method('data', return_type: "T.nilable(#{RESPONSES_NAMESPACE}::#{name}::Data)")
|
|
81
|
+
response.create_method('errors', return_type: 'T.untyped')
|
|
82
|
+
response.create_method('original_hash', return_type: 'T::Hash[String, T.untyped]')
|
|
83
|
+
declare_object(response, 'Data', operation.selections, root_type)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# One nested class per selection level. `path` is its fully qualified name,
|
|
88
|
+
# which children extend so nested references resolve.
|
|
89
|
+
sig do
|
|
90
|
+
params(parent: RBI::Scope, class_name: String, selections: T::Array[T.untyped],
|
|
91
|
+
graphql_type: T.untyped).void
|
|
92
|
+
end
|
|
93
|
+
def declare_object(parent, class_name, selections, graphql_type)
|
|
94
|
+
fields = collect_fields(selections, graphql_type)
|
|
95
|
+
|
|
96
|
+
parent.create_class(class_name) do |scope|
|
|
97
|
+
fields.each do |field|
|
|
98
|
+
scope.create_method(field.fetch(:method), return_type: field.fetch(:type),
|
|
99
|
+
comments: field.fetch(:comments))
|
|
100
|
+
nested = field[:nested]
|
|
101
|
+
next unless nested
|
|
102
|
+
|
|
103
|
+
declare_object(scope, nested.fetch(:class_name), nested.fetch(:selections),
|
|
104
|
+
nested.fetch(:type))
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Flatten a selection set into field descriptors, following inline fragments.
|
|
110
|
+
#
|
|
111
|
+
# A union or interface selection contributes every branch's fields, each
|
|
112
|
+
# made nilable: graphql-client returns one object whatever the `__typename`,
|
|
113
|
+
# and only the branch that matched carries values.
|
|
114
|
+
sig do
|
|
115
|
+
params(selections: T::Array[T.untyped], graphql_type: T.untyped, force_nilable: T::Boolean,
|
|
116
|
+
taken: T::Set[String])
|
|
117
|
+
.returns(T::Array[T::Hash[Symbol, T.untyped]])
|
|
118
|
+
end
|
|
119
|
+
def collect_fields(selections, graphql_type, force_nilable: false, taken: Set.new)
|
|
120
|
+
selections.flat_map do |selection|
|
|
121
|
+
case selection
|
|
122
|
+
when GraphQL::Language::Nodes::Field
|
|
123
|
+
field = describe_field(selection, graphql_type, force_nilable, taken)
|
|
124
|
+
field ? [field] : []
|
|
125
|
+
when GraphQL::Language::Nodes::InlineFragment
|
|
126
|
+
branch = selection.type ? lookup_type(selection.type.name) : graphql_type
|
|
127
|
+
next [] if branch.nil?
|
|
128
|
+
|
|
129
|
+
collect_fields(selection.selections, branch, force_nilable: true, taken: taken)
|
|
130
|
+
else
|
|
131
|
+
[]
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
sig do
|
|
137
|
+
params(selection: GraphQL::Language::Nodes::Field, graphql_type: T.untyped,
|
|
138
|
+
force_nilable: T::Boolean, taken: T::Set[String])
|
|
139
|
+
.returns(T.nilable(T::Hash[Symbol, T.untyped]))
|
|
140
|
+
end
|
|
141
|
+
def describe_field(selection, graphql_type, force_nilable, taken)
|
|
142
|
+
name = selection.alias || selection.name
|
|
143
|
+
method = ActiveSupport::Inflector.underscore(name)
|
|
144
|
+
return nil if taken.include?(method)
|
|
145
|
+
|
|
146
|
+
taken << method
|
|
147
|
+
return { method: method, type: '::String', comments: [], nested: nil } if name == '__typename'
|
|
148
|
+
|
|
149
|
+
field = graphql_type.respond_to?(:fields) ? graphql_type.fields[selection.name] : nil
|
|
150
|
+
return { method: method, type: 'T.untyped', comments: [], nested: nil } if field.nil?
|
|
151
|
+
|
|
152
|
+
build_field(selection, field, method, name, force_nilable, taken)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
sig do
|
|
156
|
+
params(selection: GraphQL::Language::Nodes::Field, field: T.untyped, method: String,
|
|
157
|
+
name: String, force_nilable: T::Boolean, taken: T::Set[String])
|
|
158
|
+
.returns(T::Hash[Symbol, T.untyped])
|
|
159
|
+
end
|
|
160
|
+
def build_field(selection, field, method, name, force_nilable, taken)
|
|
161
|
+
unwrapped = field.type.unwrap
|
|
162
|
+
nilable = force_nilable || !field.type.non_null?
|
|
163
|
+
|
|
164
|
+
if selection.selections.empty?
|
|
165
|
+
return { method: method, type: wrap(scalar_type(unwrapped), field.type, nilable),
|
|
166
|
+
comments: [comment("`#{name}`: #{field.type.to_type_signature}")], nested: nil }
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
class_name = ActiveSupport::Inflector.camelize(method)
|
|
170
|
+
class_name = "#{class_name}Object" if taken.include?("class:#{class_name}")
|
|
171
|
+
taken << "class:#{class_name}"
|
|
172
|
+
|
|
173
|
+
{ method: method, type: wrap(class_name, field.type, nilable),
|
|
174
|
+
comments: [comment("`#{name}`: #{field.type.to_type_signature}")],
|
|
175
|
+
nested: { class_name: class_name, selections: selection.selections,
|
|
176
|
+
type: unwrapped } }
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Apply the GraphQL type's list and nullability wrappers to `inner`.
|
|
180
|
+
#
|
|
181
|
+
# `[Ledger!]!` becomes `T::Array[X]`, `[Ledger]` becomes
|
|
182
|
+
# `T::Array[T.nilable(X)]`. `T.untyped` already includes nil, so it is never
|
|
183
|
+
# wrapped again.
|
|
184
|
+
sig { params(inner: String, graphql_type: T.untyped, nilable: T::Boolean).returns(String) }
|
|
185
|
+
def wrap(inner, graphql_type, nilable)
|
|
186
|
+
bare = graphql_type.non_null? ? graphql_type.of_type : graphql_type
|
|
187
|
+
type = if bare.list?
|
|
188
|
+
"T::Array[#{nilable_unless(inner, bare.of_type.non_null?)}]"
|
|
189
|
+
else
|
|
190
|
+
inner
|
|
191
|
+
end
|
|
192
|
+
nilable_unless(type, !nilable)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
sig { params(type: String, non_null: T::Boolean).returns(String) }
|
|
196
|
+
def nilable_unless(type, non_null)
|
|
197
|
+
non_null || type == 'T.untyped' ? type : "T.nilable(#{type})"
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
sig { params(unwrapped: T.untyped).returns(String) }
|
|
201
|
+
def scalar_type(unwrapped)
|
|
202
|
+
Helpers::GraphqlSorbetTypes.translate(unwrapped.graphql_name, scalars: SCALARS)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
sig { params(name: String).returns(T.untyped) }
|
|
206
|
+
def lookup_type(name)
|
|
207
|
+
schema.types[name]
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
sig { returns(T.untyped) }
|
|
211
|
+
def schema
|
|
212
|
+
FragmentGraphQl::FragmentSchema
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
sig { params(text: String).returns(RBI::Comment) }
|
|
216
|
+
def comment(text)
|
|
217
|
+
RBI::Comment.new(text)
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|