carddb 0.4.2 → 0.4.5

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.
@@ -1,10 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'json'
4
+
3
5
  module CardDB
4
6
  module Resources
5
7
  # Decks resource for hosted decks and external deck hydration.
6
8
  # rubocop:disable Metrics/ClassLength
7
9
  class Decks < Base
10
+ JSON_OBJECT_INPUT_KEYS = %w[metadata annotations].freeze
11
+
8
12
  # List decks owned by the current account or API application.
9
13
  def list_mine(first: nil, after: nil, include_archived: nil, cache: nil)
10
14
  query = QueryBuilder.list_my_decks(first: first, after: after, include_archived: include_archived)
@@ -91,14 +95,23 @@ module CardDB
91
95
  end
92
96
 
93
97
  # Fetch by UUID using the ownership-aware deck API.
94
- def get(id, cache: nil)
95
- key = cache_key('decks', 'get', id: id)
98
+ def get(id, read_state:, version: nil, cache: nil)
99
+ variables = build_variables(id: id, readState: read_state, version: version)
100
+ key = cache_key('decks', 'get', **variables)
96
101
  with_cache(key, resource: :decks, cache: cache) do
97
- data = connection.execute(QueryBuilder.deck, { id: id })
102
+ data = connection.execute(QueryBuilder.deck, variables)
98
103
  data['deck'] ? Deck.new(data['deck'], client: client) : nil
99
104
  end
100
105
  end
101
106
 
107
+ def get_draft(id, cache: nil)
108
+ get(id, read_state: 'DRAFT', cache: cache)
109
+ end
110
+
111
+ def get_published(id, version: nil, cache: nil)
112
+ get(id, read_state: 'PUBLISHED', version: version, cache: cache)
113
+ end
114
+
102
115
  # Fetch one deck owned by the current account or API application.
103
116
  def fetch_mine(id, cache: nil)
104
117
  key = cache_key('decks', 'fetch_mine', id: id)
@@ -150,14 +163,23 @@ module CardDB
150
163
  end
151
164
 
152
165
  # Fetch by external reference using the ownership-aware deckByExternalRef API.
153
- def get_by_external_ref(external_ref:, cache: nil)
154
- key = cache_key('decks', 'get_by_external_ref', external_ref: external_ref)
166
+ def get_by_external_ref(external_ref:, read_state:, version: nil, cache: nil)
167
+ variables = build_variables(externalRef: external_ref, readState: read_state, version: version)
168
+ key = cache_key('decks', 'get_by_external_ref', **variables)
155
169
  with_cache(key, resource: :decks, cache: cache) do
156
- data = connection.execute(QueryBuilder.deck_by_external_ref, { externalRef: external_ref })
170
+ data = connection.execute(QueryBuilder.deck_by_external_ref, variables)
157
171
  data['deckByExternalRef'] ? Deck.new(data['deckByExternalRef'], client: client) : nil
158
172
  end
159
173
  end
160
174
 
175
+ def get_draft_by_external_ref(external_ref:, cache: nil)
176
+ get_by_external_ref(external_ref: external_ref, read_state: 'DRAFT', cache: cache)
177
+ end
178
+
179
+ def get_published_by_external_ref(external_ref:, version: nil, cache: nil)
180
+ get_by_external_ref(external_ref: external_ref, read_state: 'PUBLISHED', version: version, cache: cache)
181
+ end
182
+
161
183
  # Fetch one immutable published deck version.
162
184
  def fetch_version(id, cache: nil)
163
185
  key = cache_key('decks', 'fetch_version', id: id)
@@ -185,33 +207,6 @@ module CardDB
185
207
  )
186
208
  end
187
209
 
188
- # Fetch current draft data using a revocable preview token.
189
- def preview(token:, cache: nil)
190
- key = cache_key('decks', 'preview', token: token)
191
- with_cache(key, resource: :decks, cache: cache) do
192
- data = connection.execute(QueryBuilder.deck_preview, { token: token })
193
- data['deckPreview'] ? Deck.new(data['deckPreview'], client: client) : nil
194
- end
195
- end
196
-
197
- # Fetch published embed data using a revocable embed token.
198
- def embed(token:, cache: nil)
199
- key = cache_key('decks', 'embed', token: token)
200
- with_cache(key, resource: :decks, cache: cache) do
201
- data = connection.execute(QueryBuilder.deck_embed, { token: token })
202
- data['deckEmbed'] ? Deck.new(data['deckEmbed'], client: client) : nil
203
- end
204
- end
205
-
206
- # Fetch token-authorized published data using an exchanged access token.
207
- def access(token:, cache: nil)
208
- key = cache_key('decks', 'access', token: token)
209
- with_cache(key, resource: :decks, cache: cache) do
210
- data = connection.execute(QueryBuilder.deck_access, { token: token })
211
- data['deckAccess'] ? Deck.new(data['deckAccess'], client: client) : nil
212
- end
213
- end
214
-
215
210
  def draft_diff(id:, cache: nil)
216
211
  key = cache_key('decks', 'draft_diff', id: id)
217
212
  with_cache(key, resource: :decks, cache: cache) do
@@ -239,9 +234,10 @@ module CardDB
239
234
  end
240
235
 
241
236
  def hydrate_deck_entries(input:, cache: nil)
242
- key = cache_key('decks', 'hydrate_deck_entries', input: input)
237
+ normalized_input = normalize_deck_json_input(input)
238
+ key = cache_key('decks', 'hydrate_deck_entries', input: normalized_input)
243
239
  with_cache(key, resource: :decks, cache: cache) do
244
- data = connection.execute(QueryBuilder.deck_hydrate_entries, { input: input })
240
+ data = connection.execute(QueryBuilder.deck_hydrate_entries, { input: normalized_input })
245
241
  DeckHydrateEntriesPayload.new(data['deckHydrateEntries'], client: client)
246
242
  end
247
243
  end
@@ -293,31 +289,6 @@ module CardDB
293
289
  end
294
290
  end
295
291
 
296
- # List draft preview tokens for a deck.
297
- def preview_tokens(deck_id:, cache: nil)
298
- key = cache_key('decks', 'preview_tokens', deck_id: deck_id)
299
- with_cache(key, resource: :decks, cache: cache) do
300
- data = connection.execute(QueryBuilder.deck_preview_tokens, { deckId: deck_id })
301
- (data['deckPreviewTokens'] || []).map { |token| DeckPreviewToken.new(token, client: client) }
302
- end
303
- end
304
-
305
- def embed_tokens(deck_id:, cache: nil)
306
- key = cache_key('decks', 'embed_tokens', deck_id: deck_id)
307
- with_cache(key, resource: :decks, cache: cache) do
308
- data = connection.execute(QueryBuilder.deck_embed_tokens, { deckId: deck_id })
309
- (data['deckEmbedTokens'] || []).map { |token| DeckEmbedToken.new(token, client: client) }
310
- end
311
- end
312
-
313
- def access_token_issuers(deck_id:, cache: nil)
314
- key = cache_key('decks', 'access_token_issuers', deck_id: deck_id)
315
- with_cache(key, resource: :decks, cache: cache) do
316
- data = connection.execute(QueryBuilder.deck_access_token_issuers, { deckId: deck_id })
317
- (data['deckAccessTokenIssuers'] || []).map { |issuer| DeckAccessTokenIssuer.new(issuer, client: client) }
318
- end
319
- end
320
-
321
292
  def api_application_accesses(deck_id:, include_revoked: nil, cache: nil)
322
293
  variables = build_variables(deckId: deck_id, includeRevoked: include_revoked)
323
294
  key = cache_key('decks', 'api_application_accesses', **variables)
@@ -342,13 +313,13 @@ module CardDB
342
313
 
343
314
  # Create a hosted deck.
344
315
  def create(input:)
345
- data = connection.execute(QueryBuilder.create_deck, { input: input })
316
+ data = connection.execute(QueryBuilder.create_deck, { input: normalize_deck_json_input(input) })
346
317
  Deck.new(data['deckCreate'], client: client)
347
318
  end
348
319
 
349
320
  # Update a hosted deck.
350
321
  def update(id:, input:)
351
- data = connection.execute(QueryBuilder.update_deck, { id: id, input: input })
322
+ data = connection.execute(QueryBuilder.update_deck, { id: id, input: normalize_deck_json_input(input) })
352
323
  Deck.new(data['deckUpdate'], client: client)
353
324
  end
354
325
 
@@ -360,7 +331,7 @@ module CardDB
360
331
  def upsert_by_external_ref(input:)
361
332
  config.require_secret_credential!('decks.upsert_by_external_ref')
362
333
 
363
- data = connection.execute(QueryBuilder.upsert_deck_by_external_ref, { input: input })
334
+ data = connection.execute(QueryBuilder.upsert_deck_by_external_ref, { input: normalize_deck_json_input(input) })
364
335
  DeckUpsertByExternalRefPayload.new(data['deckUpsertByExternalRef'], client: client)
365
336
  end
366
337
 
@@ -380,12 +351,12 @@ module CardDB
380
351
  end
381
352
 
382
353
  def add_entry(input:)
383
- data = connection.execute(QueryBuilder.add_deck_entry, { input: input })
354
+ data = connection.execute(QueryBuilder.add_deck_entry, { input: normalize_deck_json_input(input) })
384
355
  DeckEntryMutationPayload.new(data['deckEntryAdd'], client: client)
385
356
  end
386
357
 
387
358
  def update_entry(id:, input:)
388
- data = connection.execute(QueryBuilder.update_deck_entry, { id: id, input: input })
359
+ data = connection.execute(QueryBuilder.update_deck_entry, { id: id, input: normalize_deck_json_input(input) })
389
360
  DeckEntryMutationPayload.new(data['deckEntryUpdate'], client: client)
390
361
  end
391
362
 
@@ -403,7 +374,7 @@ module CardDB
403
374
  end
404
375
 
405
376
  def replace_entries(deck_id:, input:)
406
- data = connection.execute(QueryBuilder.replace_deck_entries, { deckId: deck_id, input: input })
377
+ data = connection.execute(QueryBuilder.replace_deck_entries, { deckId: deck_id, input: normalize_deck_json_input(input) })
407
378
  DeckEntriesReplacePayload.new(data['deckEntriesReplace'], client: client)
408
379
  end
409
380
 
@@ -522,47 +493,27 @@ module CardDB
522
493
  end
523
494
  # rubocop:enable Naming/PredicateMethod
524
495
 
525
- # Create a revocable draft preview token.
526
- def create_preview_token(input:)
527
- data = connection.execute(QueryBuilder.create_deck_preview_token, { input: input })
528
- DeckPreviewTokenCreatePayload.new(data['deckPreviewTokenCreate'], client: client)
529
- end
496
+ def create_token_issuer(input:)
497
+ config.require_secret_credential!('decks.create_token_issuer')
530
498
 
531
- # Revoke a draft preview token.
532
- # rubocop:disable Naming/PredicateMethod
533
- def revoke_preview_token(id:)
534
- data = connection.execute(QueryBuilder.revoke_deck_preview_token, { id: id })
535
- !!data['deckPreviewTokenRevoke']
536
- end
537
- # rubocop:enable Naming/PredicateMethod
538
-
539
- def create_embed_token(input:)
540
- data = connection.execute(QueryBuilder.create_deck_embed_token, { input: input })
541
- DeckEmbedTokenCreatePayload.new(data['deckEmbedTokenCreate'], client: client)
499
+ data = connection.execute(QueryBuilder.create_deck_token_issuer, { input: input })
500
+ DeckTokenIssuer.new(data['deckTokenIssuerCreate'], client: client)
542
501
  end
543
502
 
544
503
  # rubocop:disable Naming/PredicateMethod
545
- def revoke_embed_token(id:)
546
- data = connection.execute(QueryBuilder.revoke_deck_embed_token, { id: id })
547
- !!data['deckEmbedTokenRevoke']
548
- end
549
- # rubocop:enable Naming/PredicateMethod
504
+ def revoke_token_issuer(id:)
505
+ config.require_secret_credential!('decks.revoke_token_issuer')
550
506
 
551
- def create_access_token_issuer(input:)
552
- data = connection.execute(QueryBuilder.create_deck_access_token_issuer, { input: input })
553
- DeckAccessTokenIssuer.new(data['deckAccessTokenIssuerCreate'], client: client)
554
- end
555
-
556
- # rubocop:disable Naming/PredicateMethod
557
- def revoke_access_token_issuer(id:)
558
- data = connection.execute(QueryBuilder.revoke_deck_access_token_issuer, { id: id })
559
- !!data['deckAccessTokenIssuerRevoke']
507
+ data = connection.execute(QueryBuilder.revoke_deck_token_issuer, { id: id })
508
+ !!data['deckTokenIssuerRevoke']
560
509
  end
561
510
  # rubocop:enable Naming/PredicateMethod
562
511
 
563
- def revoke_access_token_issuer_signing_key(id:)
564
- data = connection.execute(QueryBuilder.revoke_deck_access_token_issuer_signing_key, { id: id })
565
- DeckAccessTokenIssuer.new(data['deckAccessTokenIssuerSigningKeyRevoke'], client: client)
512
+ def revoke_token_issuer_signing_key(id:)
513
+ config.require_secret_credential!('decks.revoke_token_issuer_signing_key')
514
+
515
+ data = connection.execute(QueryBuilder.revoke_deck_token_issuer_signing_key, { id: id })
516
+ DeckTokenIssuer.new(data['deckTokenIssuerSigningKeyRevoke'], client: client)
566
517
  end
567
518
 
568
519
  def grant_api_application_access(input:)
@@ -580,27 +531,19 @@ module CardDB
580
531
  end
581
532
  # rubocop:enable Naming/PredicateMethod
582
533
 
583
- def exchange_access_token(input:)
584
- config.require_secret_credential!('decks.exchange_access_token')
534
+ def exchange_token(input:)
535
+ config.require_secret_credential!('decks.exchange_token')
585
536
 
586
- data = connection.execute(QueryBuilder.exchange_deck_access_token, { input: input })
587
- DeckAccessTokenExchangePayload.new(data['deckAccessTokenExchange'], client: client)
588
- end
589
-
590
- # Exchange the current secret API application credential for an app-owned deck session token.
591
- def exchange_session_token(input:)
592
- config.require_secret_credential!('decks.exchange_session_token')
593
-
594
- data = connection.execute(QueryBuilder.exchange_deck_session_token, { input: input })
595
- DeckSessionTokenExchangePayload.new(data['deckSessionTokenExchange'], client: client)
537
+ data = connection.execute(QueryBuilder.exchange_deck_token, { input: input })
538
+ DeckTokenExchangePayload.new(data['deckTokenExchange'], client: client)
596
539
  end
597
540
 
598
541
  # rubocop:disable Naming/PredicateMethod
599
- def revoke_access_token(id:)
600
- config.require_secret_credential!('decks.revoke_access_token')
542
+ def revoke_token(id:)
543
+ config.require_secret_credential!('decks.revoke_token')
601
544
 
602
- data = connection.execute(QueryBuilder.revoke_deck_access_token, { id: id })
603
- !!data['deckAccessTokenRevoke']
545
+ data = connection.execute(QueryBuilder.revoke_deck_token, { id: id })
546
+ !!data['deckTokenRevoke']
604
547
  end
605
548
  # rubocop:enable Naming/PredicateMethod
606
549
 
@@ -650,6 +593,42 @@ module CardDB
650
593
 
651
594
  private
652
595
 
596
+ def normalize_deck_json_input(input)
597
+ normalize_deck_json_value(input, path: 'input')
598
+ end
599
+
600
+ def normalize_deck_json_value(value, path:)
601
+ return value unless value.is_a?(Hash)
602
+
603
+ value.each_with_object({}) do |(key, child), normalized|
604
+ key_name = key.to_s
605
+ child_path = "#{path}.#{key_name}"
606
+
607
+ normalized[key] = if JSON_OBJECT_INPUT_KEYS.include?(key_name)
608
+ normalize_json_object(child, child_path)
609
+ elsif key_name == 'entries' && child.is_a?(Array)
610
+ child.each_with_index.map do |entry, index|
611
+ normalize_deck_json_value(entry, path: "#{child_path}[#{index}]")
612
+ end
613
+ else
614
+ child
615
+ end
616
+ end
617
+ end
618
+
619
+ def normalize_json_object(value, path)
620
+ return value if value.nil? || value.is_a?(Hash)
621
+
622
+ if value.is_a?(String)
623
+ parsed = JSON.parse(value)
624
+ return parsed if parsed.is_a?(Hash)
625
+ end
626
+
627
+ raise ValidationError, "#{path} must be a JSON object"
628
+ rescue JSON::ParserError
629
+ raise ValidationError, "#{path} must be a JSON object"
630
+ end
631
+
653
632
  def entry_identifier(entry)
654
633
  entry[:identifier] || entry['identifier'] || raise(ArgumentError, 'entry identifier is required')
655
634
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CardDB
4
- VERSION = '0.4.2'
4
+ VERSION = '0.4.5'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: carddb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.4.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - CardDB Team