carddb 0.3.0 → 0.3.10
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/.rspec_status +171 -151
- data/CHANGELOG.md +7 -0
- data/README.md +198 -1
- data/examples/publisher_content_pipeline.rb +80 -0
- data/lib/carddb/batch.rb +51 -2
- data/lib/carddb/client.rb +28 -0
- data/lib/carddb/collection.rb +540 -0
- data/lib/carddb/query_builder.rb +1048 -38
- data/lib/carddb/resources/datasets.rb +85 -0
- data/lib/carddb/resources/decks.rb +8 -0
- data/lib/carddb/resources/exports.rb +96 -0
- data/lib/carddb/resources/files.rb +43 -0
- data/lib/carddb/resources/games.rb +70 -0
- data/lib/carddb/resources/import_formats.rb +103 -0
- data/lib/carddb/resources/imports.rb +225 -0
- data/lib/carddb/resources/records.rb +157 -0
- data/lib/carddb/version.rb +1 -1
- data/lib/carddb.rb +32 -0
- metadata +6 -1
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'carddb'
|
|
5
|
+
|
|
6
|
+
client = CardDB::Client.new(secret_key: ENV.fetch('CARDDB_SECRET_KEY'))
|
|
7
|
+
publisher_id = ENV.fetch('CARDDB_PUBLISHER_ID')
|
|
8
|
+
game_key = ENV.fetch('CARDDB_GAME_KEY', 'example-tcg')
|
|
9
|
+
|
|
10
|
+
game = client.games.get_by_key(publisher_id: publisher_id, game_key: game_key, cache: false) ||
|
|
11
|
+
client.games.create(
|
|
12
|
+
input: {
|
|
13
|
+
publisherId: publisher_id,
|
|
14
|
+
key: game_key,
|
|
15
|
+
name: 'Example TCG',
|
|
16
|
+
visibility: 'PRIVATE'
|
|
17
|
+
}
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
client.import_formats.create(
|
|
21
|
+
input: {
|
|
22
|
+
gameId: game.id,
|
|
23
|
+
key: 'ci-json',
|
|
24
|
+
name: 'CI JSON',
|
|
25
|
+
config: { schemaVersion: 1 }
|
|
26
|
+
}
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
advanced_payload = {
|
|
30
|
+
datasets: [
|
|
31
|
+
{
|
|
32
|
+
name: 'cards',
|
|
33
|
+
schema: {
|
|
34
|
+
identifier: { type: 'STRING', isIdentifier: true },
|
|
35
|
+
name: { type: 'STRING', required: true }
|
|
36
|
+
},
|
|
37
|
+
records: [{ identifier: 'CARD-001', name: 'Example Card' }]
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
preview = client.imports.preview_game(input: { gameId: game.id, data: advanced_payload })
|
|
43
|
+
raise "Import preview blocked: #{preview.warnings.map(&:message).join(', ')}" unless preview.can_proceed?
|
|
44
|
+
|
|
45
|
+
import_job = client.imports.run_game(
|
|
46
|
+
input: {
|
|
47
|
+
gameId: game.id,
|
|
48
|
+
data: advanced_payload,
|
|
49
|
+
options: { mode: 'CREATE', onConflict: 'UPDATE' }
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
completed_import = client.imports.wait_for_game_job(import_job.id)
|
|
53
|
+
raise completed_import.error_message unless completed_import.completed?
|
|
54
|
+
|
|
55
|
+
dataset = client.datasets.get_by_key(game_id: game.id, dataset_key: 'cards')
|
|
56
|
+
raise 'cards dataset was not created' unless dataset
|
|
57
|
+
|
|
58
|
+
upsert = client.records.upsert_batch(
|
|
59
|
+
input: {
|
|
60
|
+
datasetId: dataset.id,
|
|
61
|
+
records: [{ identifier: 'CARD-002', name: 'Second Example Card' }],
|
|
62
|
+
options: { mode: 'STRICT', onConflict: 'UPDATE', dryRun: true }
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
raise "Upsert validation failed: #{upsert.dry_run_result.errors.map(&:index).join(', ')}" if upsert.dry_run_result&.errors&.any?
|
|
67
|
+
|
|
68
|
+
delete_preview = client.records.delete_batch(
|
|
69
|
+
input: {
|
|
70
|
+
datasetId: dataset.id,
|
|
71
|
+
reconcileIdentifiers: %w[CARD-001 CARD-002],
|
|
72
|
+
dryRun: true
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
puts "Would delete #{delete_preview.matched_count} records"
|
|
76
|
+
|
|
77
|
+
export_job = client.exports.run(input: { datasetId: dataset.id, format: 'JSON' })
|
|
78
|
+
export_result = client.exports.wait_for_job(export_job.id)
|
|
79
|
+
export_with_fresh_url = client.exports.refresh_url(id: export_result.id)
|
|
80
|
+
puts export_with_fresh_url.download_url
|
data/lib/carddb/batch.rb
CHANGED
|
@@ -52,6 +52,13 @@ module CardDB
|
|
|
52
52
|
BatchProxy.new(self, :records)
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
+
# Access the Decks resource for batching
|
|
56
|
+
#
|
|
57
|
+
# @return [BatchProxy]
|
|
58
|
+
def decks
|
|
59
|
+
BatchProxy.new(self, :decks)
|
|
60
|
+
end
|
|
61
|
+
|
|
55
62
|
# Add an operation to the batch
|
|
56
63
|
#
|
|
57
64
|
# @param resource [Symbol] The resource type
|
|
@@ -120,6 +127,8 @@ module CardDB
|
|
|
120
127
|
build_dataset_query(op, index)
|
|
121
128
|
when :records
|
|
122
129
|
build_record_query(op, index)
|
|
130
|
+
when :decks
|
|
131
|
+
build_deck_query(op, index)
|
|
123
132
|
else
|
|
124
133
|
raise ArgumentError, "Unknown resource: #{op[:resource]}"
|
|
125
134
|
end
|
|
@@ -201,6 +210,27 @@ module CardDB
|
|
|
201
210
|
end
|
|
202
211
|
end
|
|
203
212
|
|
|
213
|
+
def build_deck_query(op, index)
|
|
214
|
+
case op[:method]
|
|
215
|
+
when :fetch
|
|
216
|
+
var_name = "deckId#{index}"
|
|
217
|
+
query = "#{op[:alias]}: fetchDeck(id: $#{var_name}) { #{deck_fields} }"
|
|
218
|
+
[query, { var_name => op[:args][0] }, { var_name => 'UUID' }]
|
|
219
|
+
when :get
|
|
220
|
+
if op[:kwargs][:external_ref]
|
|
221
|
+
var_name = "deckExternalRef#{index}"
|
|
222
|
+
query = "#{op[:alias]}: deckByExternalRef(externalRef: $#{var_name}) { #{deck_fields} }"
|
|
223
|
+
[query, { var_name => op[:kwargs][:external_ref] }, { var_name => 'String' }]
|
|
224
|
+
else
|
|
225
|
+
var_name = "deckId#{index}"
|
|
226
|
+
query = "#{op[:alias]}: deck(id: $#{var_name}) { #{deck_fields} }"
|
|
227
|
+
[query, { var_name => op[:args][0] }, { var_name => 'UUID' }]
|
|
228
|
+
end
|
|
229
|
+
else
|
|
230
|
+
raise ArgumentError, "Unsupported batch method for decks: #{op[:method]}"
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
204
234
|
def build_variable_definitions(variables)
|
|
205
235
|
return '' if variables.empty?
|
|
206
236
|
|
|
@@ -231,6 +261,8 @@ module CardDB
|
|
|
231
261
|
Dataset.new(result, client: @client)
|
|
232
262
|
when :records
|
|
233
263
|
Record.new(result, client: @client)
|
|
264
|
+
when :decks
|
|
265
|
+
Deck.new(result, client: @client)
|
|
234
266
|
else
|
|
235
267
|
result
|
|
236
268
|
end
|
|
@@ -263,6 +295,23 @@ module CardDB
|
|
|
263
295
|
def record_fields
|
|
264
296
|
'id datasetId data createdAt updatedAt dataset { id key name gameId publisherId }'
|
|
265
297
|
end
|
|
298
|
+
|
|
299
|
+
def deck_fields
|
|
300
|
+
[
|
|
301
|
+
'id ownerType ownerAccountId ownerApiApplicationId environment',
|
|
302
|
+
'createdByAccountId createdByApiApplicationId accountId apiApplicationId',
|
|
303
|
+
'gameId game { id key name publisher { id name slug } }',
|
|
304
|
+
'slug identifier title description formatKey visibility accessMode',
|
|
305
|
+
'discoverability state archivedAt deletedAt unpublishedAt',
|
|
306
|
+
'externalRefApiApplicationId externalRef externalSubjectRef',
|
|
307
|
+
'rulesetId sourceUrl metadata',
|
|
308
|
+
'entries { id datasetId recordId identifier quantity section sortOrder annotations display }',
|
|
309
|
+
'sectionDefinitions { key label description order default aliases minCards maxCards excludedFromDeckSize legalCardTypes metadata }',
|
|
310
|
+
'latestPublishedVersion { id deckId versionNumber slug identifier title description formatKey visibility state publishedAt }',
|
|
311
|
+
'publishedAt draftRevision draftUpdatedAt draftUpdatedByAccountId draftUpdatedByApiApplicationId',
|
|
312
|
+
'hasUnpublishedChanges createdAt updatedAt'
|
|
313
|
+
].join(' ')
|
|
314
|
+
end
|
|
266
315
|
end
|
|
267
316
|
|
|
268
317
|
# Proxy class for batching operations on a specific resource
|
|
@@ -279,8 +328,8 @@ module CardDB
|
|
|
279
328
|
end
|
|
280
329
|
|
|
281
330
|
# Proxy get calls to the batch
|
|
282
|
-
def get(**kwargs)
|
|
283
|
-
@batch.add_operation(resource: @resource, method: :get, kwargs: kwargs)
|
|
331
|
+
def get(*args, **kwargs)
|
|
332
|
+
@batch.add_operation(resource: @resource, method: :get, args: args, kwargs: kwargs)
|
|
284
333
|
nil # Return nil since we're batching
|
|
285
334
|
end
|
|
286
335
|
end
|
data/lib/carddb/client.rb
CHANGED
|
@@ -98,6 +98,34 @@ module CardDB
|
|
|
98
98
|
@records ||= Resources::Records.new(self, connection, config)
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
+
# Access the Import Formats resource
|
|
102
|
+
#
|
|
103
|
+
# @return [Resources::ImportFormats]
|
|
104
|
+
def import_formats
|
|
105
|
+
@import_formats ||= Resources::ImportFormats.new(self, connection, config)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Access the Imports resource
|
|
109
|
+
#
|
|
110
|
+
# @return [Resources::Imports]
|
|
111
|
+
def imports
|
|
112
|
+
@imports ||= Resources::Imports.new(self, connection, config)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Access the Exports resource
|
|
116
|
+
#
|
|
117
|
+
# @return [Resources::Exports]
|
|
118
|
+
def exports
|
|
119
|
+
@exports ||= Resources::Exports.new(self, connection, config)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Access the Files resource
|
|
123
|
+
#
|
|
124
|
+
# @return [Resources::Files]
|
|
125
|
+
def files
|
|
126
|
+
@files ||= Resources::Files.new(self, connection, config)
|
|
127
|
+
end
|
|
128
|
+
|
|
101
129
|
# Access the Decks resource
|
|
102
130
|
#
|
|
103
131
|
# @return [Resources::Decks]
|