uri_service 0.2.12 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c6dfdd9146dcc210aeddc11463baa748805e8f80
4
- data.tar.gz: 495d0322eae8b105d97af658c41f6be059b895e3
3
+ metadata.gz: 5012df111508e186ecb439067488e70ec5e511e5
4
+ data.tar.gz: af2dbf25aec0882474d1a8909cc26fd81007b1b8
5
5
  SHA512:
6
- metadata.gz: 16ce2155b663b3f042bf6530e573ece4b96e6552d1f11e6d4b74347e359ed850ce4aec9264245bdd14ee1cbc858d5552d762ab57ab62f04071e42281449834bc
7
- data.tar.gz: 36acf7e9108320b1d44da61cd5758b842cd3627bfe78a02ce2e8015df99576ea878254f44428dd0d4344fb6599cf94dc77cae8a1b0802e691277eb7cfa1d0a7f
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)
@@ -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
- VALID_URI_REGEX = /\A#{URI::regexp(['http', 'https'])}\z/
7
- CORE_FIELD_NAMES = ['uri', 'vocabulary_string_key', 'value', 'is_local']
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[:is_local],
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
- TrueClass :is_local, default: false
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(vocabulary_string_key, value, term_uri, additional_fields={})
162
- return self.create_term_impl(vocabulary_string_key, value, term_uri, additional_fields, false, false)
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
- # Getting a duplicate UUID from SecureRandom.uuid is EXTREMELY unlikely, but we'll account for it just in case (by making a few more attempts).
175
- 5.times {
176
- begin
177
- return self.create_term_impl(vocabulary_string_key, value, term_uri, additional_fields, true, raise_error_if_local_term_value_exists_in_vocabulary)
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
- additional_fields.stringify_keys!
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
- #Ensure that vocabulary with vocabulary_string_key exists
192
- if self.find_vocabulary(vocabulary_string_key).nil?
193
- raise UriService::NonExistentVocabularyError, "There is no vocabulary with string key: " + vocabulary_string_key
194
- end
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
- @db.transaction do
201
-
202
- value_hash = Digest::SHA256.hexdigest(value)
203
-
204
- if raise_error_if_local_term_value_exists_in_vocabulary && @db[UriService::TERMS].where(value_hash: value_hash, vocabulary_string_key: vocabulary_string_key).count > 0
205
- raise UriService::DisallowedDuplicateLocalTermValueError, "A local term already exists with the value #{value}. This is not allowed when param raise_error_if_local_term_value_exists_in_vocabulary == true."
206
- end
207
-
208
- begin
209
- @db[UriService::TERMS].insert(
210
- is_local: is_local,
211
- uri: term_uri,
212
- uri_hash: Digest::SHA256.hexdigest(term_uri),
213
- value: value,
214
- value_hash: value_hash,
215
- vocabulary_string_key: vocabulary_string_key,
216
- additional_fields: JSON.generate(additional_fields)
217
- )
218
- send_term_to_solr(vocabulary_string_key, value, term_uri, additional_fields, is_local)
219
- rescue Sequel::UniqueConstraintViolation
220
- raise UriService::ExistingUriError, "A term already exists with uri: " + term_uri + " (conflict found via uri_hash check)"
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 generate_frozen_term_hash(vocabulary_string_key, value, uri, additional_fields, is_local)
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['is_local'] = is_local
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, is_local)
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['is_local'] = is_local
231
+ doc['type'] = type
249
232
  doc['vocabulary_string_key'] = vocabulary_string_key
250
233
 
251
- additional_fields.each do |key, val|
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
- if obj.is_a?(Array)
260
- # Note boolean arrays aren't supported because they don't seem useful in this context
261
- if obj[0].is_a?(Fixnum)
262
- return '_isim'
263
- else
264
- # Treat like a string array
265
- return '_ssim'
266
- end
267
- else
268
- if obj.is_a?(String)
269
- return '_ssi'
270
- elsif obj.is_a?(TrueClass) || obj.is_a?(FalseClass)
271
- return '_bsi'
272
- elsif obj.is_a?(Fixnum)
273
- return '_isi'
274
- else
275
- raise UriService::UnsupportedObjectTypeError, "Unable to determine solr suffix for unsupported object type: #{obj.class.name}"
276
- end
277
- end
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, term_uri, additional_fields, is_local, commit=true)
282
- doc = create_term_solr_doc(vocabulary_string_key, value, term_uri, additional_fields, is_local)
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 validate_additional_fields(additional_fields)
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 first term matching the specified conditions
312
- def find_term_by(opts)
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 => 1,
328
- :sort => 'score desc, value_ssort asc, uri asc' # For consistent sorting
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']['numFound'] == 1
331
- return term_solr_doc_to_frozen_term_hash(response['response']['docs'].first)
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
- is_local = term_solr_doc.delete('is_local')
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, is_local)
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(term_uri, commit=true)
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: term_uri).delete
423
+ @db[UriService::TERMS].where(uri: uri).delete
440
424
  @rsolr_pool.with do |rsolr|
441
- rsolr.delete_by_query('uri:' + UriService.solr_escape(term_uri))
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(term_uri, opts, merge_additional_fields=true)
448
+ def update_term(uri, opts, merge_additional_fields=true)
465
449
  self.handle_database_disconnect do
466
- dataset = @db[UriService::TERMS].where(uri: term_uri)
467
- raise UriService::NonExistentUriError, "No term found with uri: " + term_uri if dataset.count == 0
468
-
469
- term_db_row = dataset.first
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
- validate_additional_fields(new_additional_fields)
469
+ validate_additional_field_keys(new_additional_fields)
483
470
 
484
471
  @db.transaction do
485
- dataset.update(value: new_value, value_hash: Digest::SHA256.hexdigest(new_value), additional_fields: JSON.generate(new_additional_fields))
486
- self.send_term_to_solr(term_db_row[:vocabulary_string_key], new_value, term_uri, new_additional_fields, term_db_row[:is_local])
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, term_uri, new_additional_fields, term_db_row[:is_local])
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
@@ -0,0 +1,7 @@
1
+ module UriService
2
+ module TermType
3
+ EXTERNAL = 'external'
4
+ LOCAL = 'local'
5
+ TEMPORARY = 'temporary'
6
+ end
7
+ end
@@ -1,6 +1,6 @@
1
1
  module UriService
2
2
 
3
- VERSION = '0.2.12'
3
+ VERSION = '0.3.0'
4
4
 
5
5
  def self.version
6
6
  VERSION
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.2.12
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: