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.
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: 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(currency: $balanceCurrency, at: $balanceAt)
711
- balance(currency: $balanceCurrency, at: $balanceAt)
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(at: $balanceAt) {
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
- $ownBalanceConsistencyMode: ReadBalanceConsistencyMode
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(ledgerEntryGroup: {
916
- ledger: { ik: $ledgerIk },
917
- key: $groupKey,
918
- value: $groupValue,
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
- equalTo: $entryType
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
- path
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
- first: $first
1148
- after: $after
1149
- last: $last
1150
- before: $before
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