uri_service 0.2.12 → 0.3.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/README.md +5 -0
- data/lib/uri_service.rb +3 -1
- data/lib/uri_service/client.rb +196 -130
- data/lib/uri_service/term_type.rb +7 -0
- data/lib/uri_service/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5012df111508e186ecb439067488e70ec5e511e5
|
4
|
+
data.tar.gz: af2dbf25aec0882474d1a8909cc26fd81007b1b8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8a9c898ce08c8f34a2ca4d23f088b24c9d735cd3ac2ecf1f565bb47584ca2e46a333d89b956da404f56b4f5d517c8ee3625b16a45b555e60ee0fc5edeea4f704
|
7
|
+
data.tar.gz: f6b423ecf9d89676b3661fd027ca5ba14bca37affffe3bb9e16415559d0de451b637554e7fc774b46339a4d108cb4d9b1f63f94690eeb93d7f080cf9fb2289ee
|
data/README.md
CHANGED
@@ -15,6 +15,7 @@ gem install uri_service
|
|
15
15
|
```ruby
|
16
16
|
UriService::init({
|
17
17
|
'local_uri_base' => 'http://id.example.com/term/',
|
18
|
+
'temporary_uri_base' => 'com:example:id:temporary:',
|
18
19
|
'solr' => {
|
19
20
|
'url' => 'http://localhost:8983/solr/uri_service_test',
|
20
21
|
'pool_size' => 5,
|
@@ -36,6 +37,7 @@ UriService.client.do_stuff(...)
|
|
36
37
|
```ruby
|
37
38
|
client = UriService::Client.new({
|
38
39
|
'local_uri_base' => 'http://id.example.com/term/',
|
40
|
+
'temporary_uri_base' => 'com:example:id:temporary:',
|
39
41
|
'solr' => {
|
40
42
|
'url' => 'http://localhost:8983/solr/uri_service_test',
|
41
43
|
'pool_size' => 5,
|
@@ -65,6 +67,7 @@ Note that the database that you specify here does not have to be the same databa
|
|
65
67
|
```yaml
|
66
68
|
development:
|
67
69
|
local_uri_base: 'http://id.example.com/term/'
|
70
|
+
temporary_uri_base: 'com:example:id:temporary:'
|
68
71
|
solr:
|
69
72
|
url: 'http://localhost:8983/solr/uri_service_development'
|
70
73
|
pool_size: 5
|
@@ -77,6 +80,7 @@ development:
|
|
77
80
|
|
78
81
|
test:
|
79
82
|
local_uri_base: 'http://id.example.com/term/'
|
83
|
+
temporary_uri_base: 'com:example:id:temporary:'
|
80
84
|
solr:
|
81
85
|
url: 'http://localhost:8983/solr/uri_service_test'
|
82
86
|
pool_size: 5
|
@@ -89,6 +93,7 @@ test:
|
|
89
93
|
|
90
94
|
production:
|
91
95
|
local_uri_base: 'http://id.example.com/term/'
|
96
|
+
temporary_uri_base: 'com:example:id:temporary:'
|
92
97
|
solr:
|
93
98
|
url: 'http://localhost:9983/solr/uri_service_production'
|
94
99
|
pool_size: 5
|
data/lib/uri_service.rb
CHANGED
@@ -12,9 +12,10 @@ module UriService
|
|
12
12
|
VOCABULARIES = :vocabularies
|
13
13
|
TERM = :term
|
14
14
|
TERMS = :terms
|
15
|
+
VALID_URI_REGEX = /\A#{URI::regexp}\z/
|
15
16
|
|
16
17
|
# Initialize the main instance of UriService::Client
|
17
|
-
# opts format: { 'local_uri_base' => 'http://id.example.com/term/', 'solr' => {...solr config...}, 'database' => {...database config...} }
|
18
|
+
# opts format: { 'local_uri_base' => 'http://id.example.com/term/', temporary_uri_base: 'temporary:', 'solr' => {...solr config...}, 'database' => {...database config...} }
|
18
19
|
def self.init(opts)
|
19
20
|
if @client && @client.connected?
|
20
21
|
@client.disconnect!
|
@@ -47,6 +48,7 @@ module UriService
|
|
47
48
|
end
|
48
49
|
|
49
50
|
require "uri_service/version"
|
51
|
+
require "uri_service/term_type"
|
50
52
|
require "uri_service/client"
|
51
53
|
|
52
54
|
require 'uri_service/railtie' if defined?(Rails)
|
data/lib/uri_service/client.rb
CHANGED
@@ -1,18 +1,20 @@
|
|
1
1
|
class UriService::Client
|
2
2
|
|
3
|
-
attr_reader :db, :rsolr_pool, :local_uri_base
|
3
|
+
attr_reader :db, :rsolr_pool, :local_uri_base, :temporary_uri_base
|
4
4
|
|
5
5
|
ALPHANUMERIC_UNDERSCORE_KEY_REGEX = /\A[a-z]+[a-z0-9_]*\z/
|
6
|
-
|
7
|
-
|
6
|
+
CORE_FIELD_NAMES = ['uri', 'vocabulary_string_key', 'value', 'type']
|
7
|
+
VALID_TYPES = [UriService::TermType::EXTERNAL, UriService::TermType::LOCAL, UriService::TermType::TEMPORARY]
|
8
8
|
|
9
9
|
def initialize(opts)
|
10
10
|
raise UriService::InvalidOptsError, "Must supply opts['local_uri_base'] to initialize method." if opts['local_uri_base'].nil?
|
11
|
+
raise UriService::InvalidOptsError, "Must supply opts['temporary_uri_base'] to initialize method." if opts['temporary_uri_base'].nil?
|
11
12
|
raise UriService::InvalidOptsError, "Must supply opts['database'] to initialize method." if opts['database'].nil?
|
12
13
|
raise UriService::InvalidOptsError, "Must supply opts['solr'] to initialize method." if opts['solr'].nil?
|
13
14
|
|
14
|
-
# Set local_uri_base
|
15
|
+
# Set local_uri_base and temporary_uri_base
|
15
16
|
@local_uri_base = opts['local_uri_base']
|
17
|
+
@temporary_uri_base = opts['temporary_uri_base']
|
16
18
|
|
17
19
|
# Create DB connection pool
|
18
20
|
@db = Sequel.connect(opts['database'])
|
@@ -45,7 +47,7 @@ class UriService::Client
|
|
45
47
|
term_db_row[:value],
|
46
48
|
term_db_row[:uri],
|
47
49
|
JSON.parse(term_db_row[:additional_fields]),
|
48
|
-
term_db_row[:
|
50
|
+
term_db_row[:type],
|
49
51
|
false)
|
50
52
|
|
51
53
|
if print_progress_to_console
|
@@ -122,7 +124,7 @@ class UriService::Client
|
|
122
124
|
String :uri_hash, fixed: true, size: 64, unique: true
|
123
125
|
String :value, text: true
|
124
126
|
String :value_hash, fixed: true, size: 64
|
125
|
-
|
127
|
+
String :type, null: false
|
126
128
|
String :additional_fields, text: true
|
127
129
|
end
|
128
130
|
puts 'Created table: ' + UriService::TERMS.to_s
|
@@ -157,79 +159,60 @@ class UriService::Client
|
|
157
159
|
end
|
158
160
|
end
|
159
161
|
|
160
|
-
# Creates a new term
|
161
|
-
def create_term(
|
162
|
-
|
163
|
-
end
|
164
|
-
|
165
|
-
# Creates a new local term, auto-generating a URI
|
166
|
-
# By default, if raise_error_if_local_term_value_exists_in_vocabulary param is true, rejects the creation of a local value if that exact value already exists in the specified vocabulary
|
167
|
-
def create_local_term(vocabulary_string_key, value, additional_fields={}, raise_error_if_local_term_value_exists_in_vocabulary=true)
|
168
|
-
|
169
|
-
# Create a new URI for this local term, using the @local_uri_base
|
170
|
-
term_uri = URI(@local_uri_base)
|
171
|
-
term_uri.path += SecureRandom.uuid # Generate random UUID for local URI
|
172
|
-
term_uri = term_uri.to_s
|
162
|
+
# Creates a new term
|
163
|
+
def create_term(type, opts)
|
164
|
+
raise UriService::InvalidTermTypeError, 'Invalid type: ' + type unless VALID_TYPES.include?(type)
|
173
165
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
rescue UriService::ExistingUriError
|
179
|
-
raise UriService::ExistingUriError, "UriService generated a duplicate random UUID (via SecureRandom.uuid) and will now attempt to create another. This type of problem is EXTREMELY rare."
|
180
|
-
end
|
181
|
-
}
|
182
|
-
|
183
|
-
return nil
|
184
|
-
end
|
185
|
-
|
186
|
-
def create_term_impl(vocabulary_string_key, value, term_uri, additional_fields, is_local, raise_error_if_local_term_value_exists_in_vocabulary)
|
187
|
-
self.handle_database_disconnect do
|
166
|
+
vocabulary_string_key = opts.delete(:vocabulary_string_key)
|
167
|
+
value = opts.delete(:value)
|
168
|
+
uri = opts.delete(:uri)
|
169
|
+
additional_fields = opts.delete(:additional_fields) || {}
|
188
170
|
|
189
|
-
|
171
|
+
if type == UriService::TermType::EXTERNAL
|
172
|
+
# URI is required
|
173
|
+
raise UriService::InvalidOptsError, "A uri must be supplied for terms of type #{type}." if uri.nil?
|
190
174
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
unless term_uri =~ VALID_URI_REGEX
|
196
|
-
raise UriService::InvalidUriError, "Invalid URI supplied: #{term_uri}, with result #{(VALID_URI_REGEX.match(term_uri)).to_s}"
|
197
|
-
end
|
198
|
-
validate_additional_fields(additional_fields) # This method call raises an error if an invalid additional_field key is supplied
|
175
|
+
return create_term_impl(type, vocabulary_string_key, value, uri, additional_fields)
|
176
|
+
else
|
177
|
+
# URI should not be present
|
178
|
+
raise UriService::InvalidOptsError, "A uri cannot supplied for term type: #{type}." unless uri.nil?
|
199
179
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
uri
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
end
|
180
|
+
if type == UriService::TermType::TEMPORARY
|
181
|
+
# No two TEMPORARY terms within the same vocabulary can have the same value, so we generate a unique URI from a hash of the (vocabulary_string_key + value) to ensure uniqueness.
|
182
|
+
uri = self.generate_uri_for_temporary_term(vocabulary_string_key, value)
|
183
|
+
return create_term_impl(type, vocabulary_string_key, value, uri, additional_fields)
|
184
|
+
elsif type == UriService::TermType::LOCAL
|
185
|
+
5.times {
|
186
|
+
# We generate a unique URI for a local term from a UUID generator.
|
187
|
+
# Getting a duplicate UUID from a call to SecureRandom.uuid is EXTREMELY unlikely,
|
188
|
+
# but we'll account for it just in case by being ready to make multiple attempts.
|
189
|
+
begin
|
190
|
+
# Generate new URI for LOCAL and TEMPORARY terms
|
191
|
+
uri = URI(@local_uri_base)
|
192
|
+
uri.path += SecureRandom.uuid # Generate random UUID for local URI
|
193
|
+
uri = uri.to_s
|
194
|
+
return create_term_impl(type, vocabulary_string_key, value, uri, additional_fields)
|
195
|
+
rescue UriService::ExistingUriError
|
196
|
+
next
|
197
|
+
end
|
198
|
+
}
|
199
|
+
# Probabilistically, the error below should never be raised.
|
200
|
+
raise UriService::CouldNotGenerateUriError, "UriService generated a duplicate random UUID (via SecureRandom.uuid) too many times in a row. Probabilistically, this should never happen."
|
222
201
|
end
|
223
202
|
|
224
|
-
return generate_frozen_term_hash(vocabulary_string_key, value, term_uri, additional_fields, is_local)
|
225
203
|
end
|
226
204
|
end
|
227
205
|
|
228
|
-
def
|
206
|
+
def generate_uri_for_temporary_term(vocabulary_string_key, term_value)
|
207
|
+
uri = URI(@temporary_uri_base + Digest::SHA256.hexdigest(vocabulary_string_key + term_value))
|
208
|
+
return uri.to_s
|
209
|
+
end
|
210
|
+
|
211
|
+
def generate_frozen_term_hash(vocabulary_string_key, value, uri, additional_fields, type)
|
229
212
|
hash_to_return = {}
|
230
213
|
hash_to_return['uri'] = uri
|
231
214
|
hash_to_return['value'] = value
|
232
|
-
hash_to_return['
|
215
|
+
hash_to_return['type'] = type
|
233
216
|
hash_to_return['vocabulary_string_key'] = vocabulary_string_key
|
234
217
|
|
235
218
|
additional_fields.each do |key, val|
|
@@ -241,45 +224,43 @@ class UriService::Client
|
|
241
224
|
return hash_to_return
|
242
225
|
end
|
243
226
|
|
244
|
-
def create_term_solr_doc(vocabulary_string_key, value, uri, additional_fields,
|
227
|
+
def create_term_solr_doc(vocabulary_string_key, value, uri, additional_fields, type)
|
245
228
|
doc = {}
|
246
229
|
doc['uri'] = uri
|
247
230
|
doc['value'] = value
|
248
|
-
doc['
|
231
|
+
doc['type'] = type
|
249
232
|
doc['vocabulary_string_key'] = vocabulary_string_key
|
250
233
|
|
251
|
-
additional_fields
|
252
|
-
doc[key + self.class.get_solr_suffix_for_object(val)] = val
|
253
|
-
end
|
234
|
+
doc['additional_fields'] = JSON.generate(additional_fields)
|
254
235
|
|
255
236
|
return doc
|
256
237
|
end
|
257
238
|
|
258
|
-
def self.get_solr_suffix_for_object(obj)
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
end
|
239
|
+
#def self.get_solr_suffix_for_object(obj)
|
240
|
+
# if obj.is_a?(Array)
|
241
|
+
# # Note boolean arrays aren't supported because they don't seem useful in this context
|
242
|
+
# if obj[0].is_a?(Fixnum)
|
243
|
+
# return '_isim'
|
244
|
+
# else
|
245
|
+
# # Treat like a string array
|
246
|
+
# return '_ssim'
|
247
|
+
# end
|
248
|
+
# else
|
249
|
+
# if obj.is_a?(String)
|
250
|
+
# return '_ssi'
|
251
|
+
# elsif obj.is_a?(TrueClass) || obj.is_a?(FalseClass)
|
252
|
+
# return '_bsi'
|
253
|
+
# elsif obj.is_a?(Fixnum)
|
254
|
+
# return '_isi'
|
255
|
+
# else
|
256
|
+
# raise UriService::UnsupportedObjectTypeError, "Unable to determine solr suffix for unsupported object type: #{obj.class.name}"
|
257
|
+
# end
|
258
|
+
# end
|
259
|
+
#end
|
279
260
|
|
280
261
|
# Index the DB row term data into solr
|
281
|
-
def send_term_to_solr(vocabulary_string_key, value,
|
282
|
-
doc = create_term_solr_doc(vocabulary_string_key, value,
|
262
|
+
def send_term_to_solr(vocabulary_string_key, value, uri, additional_fields, type, commit=true)
|
263
|
+
doc = create_term_solr_doc(vocabulary_string_key, value, uri, additional_fields, type)
|
283
264
|
@rsolr_pool.with do |rsolr|
|
284
265
|
rsolr.add(doc)
|
285
266
|
rsolr.commit if commit
|
@@ -287,7 +268,7 @@ class UriService::Client
|
|
287
268
|
end
|
288
269
|
|
289
270
|
# Validates additional_fields and verifies that no reserved words are supplied
|
290
|
-
def
|
271
|
+
def validate_additional_field_keys(additional_fields)
|
291
272
|
additional_fields.each do |key, value|
|
292
273
|
if CORE_FIELD_NAMES.include?(key.to_s)
|
293
274
|
raise UriService::InvalidAdditionalFieldKeyError, "Cannot supply the key \"#{key.to_s}\" as an additional field because it is a reserved key."
|
@@ -308,8 +289,14 @@ class UriService::Client
|
|
308
289
|
end
|
309
290
|
end
|
310
291
|
|
311
|
-
# Finds the
|
312
|
-
def
|
292
|
+
# Finds the term with the given uri
|
293
|
+
def find_term_by_uri(uri)
|
294
|
+
results = self.find_terms_where({uri: uri}, 1)
|
295
|
+
return results.length == 1 ? results.first : nil
|
296
|
+
end
|
297
|
+
|
298
|
+
# Finds terms that match the specified conditions
|
299
|
+
def find_terms_where(opts, limit=10)
|
313
300
|
fqs = []
|
314
301
|
|
315
302
|
# Only search on allowed fields
|
@@ -324,14 +311,20 @@ class UriService::Client
|
|
324
311
|
response = rsolr.get('select', params: {
|
325
312
|
:q => '*:*',
|
326
313
|
:fq => fqs,
|
327
|
-
:rows =>
|
328
|
-
:sort => '
|
314
|
+
:rows => limit,
|
315
|
+
:sort => 'value_ssort asc, uri asc' # For consistent sorting
|
316
|
+
# Note: We don't sort by solr score because solr fq searches don't factor into the score
|
329
317
|
})
|
330
|
-
if response['response']['
|
331
|
-
|
318
|
+
if response['response']['docs'].length > 0
|
319
|
+
arr_to_return = []
|
320
|
+
response['response']['docs'].each do |doc|
|
321
|
+
arr_to_return << term_solr_doc_to_frozen_term_hash(doc)
|
322
|
+
end
|
323
|
+
return arr_to_return
|
324
|
+
else
|
325
|
+
return []
|
332
326
|
end
|
333
327
|
end
|
334
|
-
return nil
|
335
328
|
end
|
336
329
|
|
337
330
|
def term_solr_doc_to_frozen_term_hash(term_solr_doc)
|
@@ -339,19 +332,10 @@ class UriService::Client
|
|
339
332
|
uri = term_solr_doc.delete('uri')
|
340
333
|
vocabulary_string_key = term_solr_doc.delete('vocabulary_string_key')
|
341
334
|
value = term_solr_doc.delete('value')
|
342
|
-
|
343
|
-
additional_fields =
|
344
|
-
|
345
|
-
# Iterate through remaining keys and put them in additional_fields after removing suffixes
|
346
|
-
term_solr_doc.each do |key, val|
|
347
|
-
# Skip certain automatically added fields that aren't part of the term_hash
|
348
|
-
next if ['_version_', 'timestamp', 'score'].include?(key)
|
349
|
-
|
350
|
-
# Remove trailing '_si', '_bi', etc. if present for solr-suffixed fields
|
351
|
-
additional_fields[key.gsub(/_[^_]+$/, '')] = val
|
352
|
-
end
|
335
|
+
type = term_solr_doc.delete('type')
|
336
|
+
additional_fields = JSON.parse(term_solr_doc.delete('additional_fields'))
|
353
337
|
|
354
|
-
return generate_frozen_term_hash(vocabulary_string_key, value, uri, additional_fields,
|
338
|
+
return generate_frozen_term_hash(vocabulary_string_key, value, uri, additional_fields, type)
|
355
339
|
end
|
356
340
|
|
357
341
|
def find_terms_by_query(vocabulary_string_key, value_query, limit=10, start=0)
|
@@ -433,12 +417,12 @@ class UriService::Client
|
|
433
417
|
end
|
434
418
|
end
|
435
419
|
|
436
|
-
def delete_term(
|
420
|
+
def delete_term(uri, commit=true)
|
437
421
|
self.handle_database_disconnect do
|
438
422
|
@db.transaction do
|
439
|
-
@db[UriService::TERMS].where(uri:
|
423
|
+
@db[UriService::TERMS].where(uri: uri).delete
|
440
424
|
@rsolr_pool.with do |rsolr|
|
441
|
-
rsolr.delete_by_query('uri:' + UriService.solr_escape(
|
425
|
+
rsolr.delete_by_query('uri:' + UriService.solr_escape(uri))
|
442
426
|
rsolr.commit if commit
|
443
427
|
end
|
444
428
|
end
|
@@ -461,12 +445,15 @@ class UriService::Client
|
|
461
445
|
end
|
462
446
|
|
463
447
|
# opts format: {:value => 'new value', :additional_fields => {'key' => 'value'}}
|
464
|
-
def update_term(
|
448
|
+
def update_term(uri, opts, merge_additional_fields=true)
|
465
449
|
self.handle_database_disconnect do
|
466
|
-
|
467
|
-
raise UriService::NonExistentUriError, "No term found with uri: " +
|
468
|
-
|
469
|
-
term_db_row
|
450
|
+
term_db_row = @db[UriService::TERMS].first(uri: uri)
|
451
|
+
raise UriService::NonExistentUriError, "No term found with uri: " + uri if term_db_row.nil?
|
452
|
+
|
453
|
+
if term_db_row[:type] == UriService::TermType::TEMPORARY
|
454
|
+
# TEMPORARY terms cannot have their values, additional_fields or anything else changed
|
455
|
+
raise UriService::CannotChangeTemporaryTerm, "Temporary terms cannot be changed. Delete unusued temporary terms or create new ones."
|
456
|
+
end
|
470
457
|
|
471
458
|
new_value = opts[:value] || term_db_row[:value]
|
472
459
|
new_additional_fields = term_db_row[:additional_fields].nil? ? {} : JSON.parse(term_db_row[:additional_fields])
|
@@ -479,14 +466,14 @@ class UriService::Client
|
|
479
466
|
new_additional_fields = opts[:additional_fields]
|
480
467
|
end
|
481
468
|
end
|
482
|
-
|
469
|
+
validate_additional_field_keys(new_additional_fields)
|
483
470
|
|
484
471
|
@db.transaction do
|
485
|
-
|
486
|
-
self.send_term_to_solr(term_db_row[:vocabulary_string_key], new_value,
|
472
|
+
@db[UriService::TERMS].where(uri: uri).update(value: new_value, value_hash: Digest::SHA256.hexdigest(new_value), additional_fields: JSON.generate(new_additional_fields))
|
473
|
+
self.send_term_to_solr(term_db_row[:vocabulary_string_key], new_value, uri, new_additional_fields, term_db_row[:type])
|
487
474
|
end
|
488
475
|
|
489
|
-
return generate_frozen_term_hash(term_db_row[:vocabulary_string_key], new_value,
|
476
|
+
return generate_frozen_term_hash(term_db_row[:vocabulary_string_key], new_value, uri, new_additional_fields, term_db_row[:type])
|
490
477
|
end
|
491
478
|
end
|
492
479
|
|
@@ -513,16 +500,95 @@ class UriService::Client
|
|
513
500
|
end
|
514
501
|
end
|
515
502
|
|
503
|
+
#########################
|
504
|
+
# BEGIN PRIVATE METHODS #
|
505
|
+
#########################
|
506
|
+
|
507
|
+
private
|
508
|
+
|
509
|
+
# Backing implementation for actual term creation in db/solr.
|
510
|
+
# - Performs some data validations.
|
511
|
+
# - Ensures uniqueness of URIs in database.
|
512
|
+
# - Returns an existing TEMPORARY term if a user attempts to
|
513
|
+
# create a new TEMPORARY term with an existing value/vocabulary combo.
|
514
|
+
def create_term_impl(type, vocabulary_string_key, value, uri, additional_fields)
|
515
|
+
self.handle_database_disconnect do
|
516
|
+
|
517
|
+
if type == UriService::TermType::TEMPORARY
|
518
|
+
# If this is a TEMPORARY term, we need to ensure that the temporary
|
519
|
+
# passed in URI is a hash of the vocabulary + value, just in case this
|
520
|
+
# method is ever called directly instead of through the create_term
|
521
|
+
# wrapper method. This is to ensure that our expectations about the
|
522
|
+
# uniqueness of TEMPORARY term values is never violated.
|
523
|
+
unless uri == self.generate_uri_for_temporary_term(vocabulary_string_key, value)
|
524
|
+
raise UriService::InvalidTemporaryTermUriError, "The supplied URI was not derived from the supplied (vocabulary_string_key+value) pair."
|
525
|
+
end
|
526
|
+
|
527
|
+
# TEMPORARY terms are not meant to hold data in additional_fields.
|
528
|
+
if additional_fields.size > 0
|
529
|
+
raise UriService::InvalidOptsError, "Terms of type #{type} cannot have additional_fields."
|
530
|
+
end
|
531
|
+
end
|
532
|
+
|
533
|
+
unless uri =~ UriService::VALID_URI_REGEX
|
534
|
+
raise UriService::InvalidUriError, "Invalid URI supplied during term creation: #{uri}"
|
535
|
+
end
|
536
|
+
|
537
|
+
#Ensure that vocabulary with vocabulary_string_key exists
|
538
|
+
if self.find_vocabulary(vocabulary_string_key).nil?
|
539
|
+
raise UriService::NonExistentVocabularyError, "There is no vocabulary with string key: " + vocabulary_string_key
|
540
|
+
end
|
541
|
+
|
542
|
+
# Stringify and validate keys for additional_fields
|
543
|
+
additional_fields.stringify_keys!
|
544
|
+
validate_additional_field_keys(additional_fields) # This method call raises an error if an invalid additional_field key is supplied
|
545
|
+
|
546
|
+
@db.transaction do
|
547
|
+
value_hash = Digest::SHA256.hexdigest(value)
|
548
|
+
|
549
|
+
begin
|
550
|
+
@db[UriService::TERMS].insert(
|
551
|
+
type: type,
|
552
|
+
uri: uri,
|
553
|
+
uri_hash: Digest::SHA256.hexdigest(uri),
|
554
|
+
value: value,
|
555
|
+
value_hash: value_hash,
|
556
|
+
vocabulary_string_key: vocabulary_string_key,
|
557
|
+
additional_fields: JSON.generate(additional_fields)
|
558
|
+
)
|
559
|
+
send_term_to_solr(vocabulary_string_key, value, uri, additional_fields, type)
|
560
|
+
rescue Sequel::UniqueConstraintViolation
|
561
|
+
|
562
|
+
# If this is a new TEMPORARY term and we ran into a Sequel::UniqueConstraintViolation,
|
563
|
+
# that mean that the term already exists. We should return that existing term.
|
564
|
+
# don't create a new one. Instead, return the existing one.
|
565
|
+
if type == UriService::TermType::TEMPORARY
|
566
|
+
return self.find_term_by_uri(uri)
|
567
|
+
end
|
568
|
+
|
569
|
+
raise UriService::ExistingUriError, "A term already exists with uri: " + uri + " (conflict found via uri_hash check)"
|
570
|
+
|
571
|
+
end
|
572
|
+
|
573
|
+
return generate_frozen_term_hash(vocabulary_string_key, value, uri, additional_fields, type)
|
574
|
+
|
575
|
+
end
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
516
579
|
end
|
517
580
|
|
581
|
+
class UriService::CannotChangeTemporaryTerm < StandardError;end
|
582
|
+
class UriService::CouldNotGenerateUriError < StandardError;end
|
518
583
|
class UriService::InvalidAdditionalFieldKeyError < StandardError;end
|
519
|
-
class UriService::InvalidVocabularyStringKeyError < StandardError;end
|
520
584
|
class UriService::InvalidOptsError < StandardError;end
|
585
|
+
class UriService::InvalidTemporaryTermUriError < StandardError;end
|
586
|
+
class UriService::InvalidTermTypeError < StandardError;end
|
521
587
|
class UriService::InvalidUriError < StandardError;end
|
588
|
+
class UriService::InvalidVocabularyStringKeyError < StandardError;end
|
522
589
|
class UriService::ExistingUriError < StandardError;end
|
523
590
|
class UriService::ExistingVocabularyStringKeyError < StandardError;end
|
524
591
|
class UriService::NonExistentUriError < StandardError;end
|
525
592
|
class UriService::NonExistentVocabularyError < StandardError;end
|
526
593
|
class UriService::UnsupportedObjectTypeError < StandardError;end
|
527
|
-
class UriService::UnsupportedSearchFieldError < StandardError;end
|
528
|
-
class UriService::DisallowedDuplicateLocalTermValueError < StandardError;end
|
594
|
+
class UriService::UnsupportedSearchFieldError < StandardError;end
|
data/lib/uri_service/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: uri_service
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric O'Hanlon
|
@@ -179,6 +179,7 @@ files:
|
|
179
179
|
- lib/uri_service.rb
|
180
180
|
- lib/uri_service/client.rb
|
181
181
|
- lib/uri_service/railtie.rb
|
182
|
+
- lib/uri_service/term_type.rb
|
182
183
|
- lib/uri_service/version.rb
|
183
184
|
homepage: https://github.com/cul/uri_service
|
184
185
|
licenses:
|
@@ -206,4 +207,3 @@ specification_version: 4
|
|
206
207
|
summary: A service for registering local URIs and performing both local and remote
|
207
208
|
URI lookups.
|
208
209
|
test_files: []
|
209
|
-
has_rdoc:
|