uc3-dmp-id 0.0.97 → 0.0.99
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/uc3-dmp-id/asserter.rb +107 -0
- data/lib/uc3-dmp-id/creator.rb +1 -2
- data/lib/uc3-dmp-id/helper.rb +2 -2
- data/lib/uc3-dmp-id/updater.rb +51 -30
- data/lib/uc3-dmp-id/version.rb +1 -1
- data/lib/uc3-dmp-id/versioner.rb +32 -67
- data/lib/uc3-dmp-id.rb +1 -1
- metadata +3 -3
- data/lib/uc3-dmp-id/splicer.rb +0 -146
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e3bcba612098c1d0acd9a553f25549a198bbefb0f28c9c0ef166038382c17c07
|
4
|
+
data.tar.gz: 5ba9be2055b9c39a4c887930b5250860cd9dc986ddf4d0248d25e786f932c9fe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f3b3e728a5c532fb024dddd69a366fc9867e3a05856562c863b89059a38a320d63e70b9d0241249b2a8d419bf8898e894bd93617c681b3601ea82e701433f558
|
7
|
+
data.tar.gz: 5401aba365595519141ea7f2115366bdaf53b47183ee2fcd3ecba219c9625f8148cc1042faa8f67737f4fb750d60dfe62d2a334ed8e7449a02b8edbd2023744f
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Uc3DmpId
|
4
|
+
class AsserterError < StandardError; end
|
5
|
+
|
6
|
+
class Asserter
|
7
|
+
class << self
|
8
|
+
# Add assertions to a DMP ID - this is performed by non-provenance systems
|
9
|
+
def add(updater:, dmp:, mods:, note: nil, logger: nil)
|
10
|
+
# If the updater and provenance are the same just return the :dmp as-is
|
11
|
+
return dmp if updater.nil? || !dmp.is_a?(Hash) || !mods.is_a?(Hash) ||
|
12
|
+
updater&.gsub('PROVENANCE#', '') == dmp['dmphub_provenance_id']&.gsub('PROVENANCE#', '')
|
13
|
+
|
14
|
+
contact = mods['contact']
|
15
|
+
contributor = mods.fetch('contributor', [])
|
16
|
+
project = mods.fetch('project', [])
|
17
|
+
# Return the DMP ID as-is if there are no assertable changes
|
18
|
+
return dmp if contact.nil? && contributor.empty? && project.empty?
|
19
|
+
|
20
|
+
# Clone any existing assertions on the current DMP ID so we can manipulate them
|
21
|
+
assertions = Helper.deep_copy_dmp(obj: dmp.fetch('dmphub_assertions', []))
|
22
|
+
# Return the DMP ID as-is if the assertion is already on the record
|
23
|
+
return dmp if assertions.select { |entry| entry['provenance'] == updater && entry['assertions'] == mods }
|
24
|
+
|
25
|
+
assertions << _generate_assertion(updater: updater, mods: mods, note: note)
|
26
|
+
dmp['dmphub_assertions'] = assertions.flatten
|
27
|
+
dmp
|
28
|
+
end
|
29
|
+
|
30
|
+
# Splice together assertions made while the user was updating the DMP ID
|
31
|
+
def splice(latest_version:, modified_version:, logger: nil)
|
32
|
+
# Return the modified_version if the timestamps are the same (meaning no new assertions were made while the
|
33
|
+
# user was working on the DMP ID)
|
34
|
+
return modified_version if latest_version['dmphub_updated_at'] == modified_version['dmphub_updated_at']
|
35
|
+
|
36
|
+
# Clone any existing assertions on the current DMP ID so we can manipulate them
|
37
|
+
existing_assertions = Helper.deep_copy_dmp(obj: latest_version.fetch('dmphub_assertions', []))
|
38
|
+
incoming_assertions = Helper.deep_copy_dmp(obj: modified_version.fetch('dmphub_assertions', []))
|
39
|
+
logger.debug(message: "Existing assertions", details: existing_assertions) if logger.respond_to?(:debug)
|
40
|
+
logger.debug(message: "Incoming modifications", details: incoming_assertions) if logger.respond_to?(:debug)
|
41
|
+
|
42
|
+
# Keep any assetions that were made after the dmphub_updated_at on the incoming changes
|
43
|
+
latest_version['dmphub_assertions'] = existing_assertions.select do |entry|
|
44
|
+
!entry['timestamp'].nil? && Time.parse(entry['timestamp']) > Time.parse(modified_version['dmphub_updated_at'])
|
45
|
+
end
|
46
|
+
return latest_version unless incoming_assertions.any?
|
47
|
+
|
48
|
+
# Add any of the assertions still on the incoming record back to the latest record
|
49
|
+
incoming_assertions.each { |entry| latest_version['dmphub_assertions'] << entry }
|
50
|
+
latest_version
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
# Generate an assertion entry. For example:
|
56
|
+
#
|
57
|
+
# {
|
58
|
+
# "id": "ABCD1234",
|
59
|
+
# "provenance": "dmphub",
|
60
|
+
# "timestamp": "2023-07-07T14:50:23+00:00",
|
61
|
+
# "note": "data received from the NIH API",
|
62
|
+
# "assertions": {
|
63
|
+
# "contact": {
|
64
|
+
# "name": "Wrong Person"
|
65
|
+
# },
|
66
|
+
# "contributor": [
|
67
|
+
# {
|
68
|
+
# "name": "Jane Doe",
|
69
|
+
# "role": ["Investigation"]
|
70
|
+
# }
|
71
|
+
# ],
|
72
|
+
# "project": [
|
73
|
+
# {
|
74
|
+
# "start": "2024-01-01T00:00:00+07:00",
|
75
|
+
# "end": "2025-12-31T23:59:59+07:00"
|
76
|
+
# }
|
77
|
+
# ],
|
78
|
+
# "funding": [
|
79
|
+
# {
|
80
|
+
# "funder_id": {
|
81
|
+
# "identifier": "https://doi.org/10.13039/501100001807",
|
82
|
+
# "type": "fundref"
|
83
|
+
# },
|
84
|
+
# "funding_status": "granted",
|
85
|
+
# "grant_id": {
|
86
|
+
# "identifier": "2019/22702-3",
|
87
|
+
# "type": "other"
|
88
|
+
# }
|
89
|
+
# }
|
90
|
+
# ]
|
91
|
+
# }
|
92
|
+
# }
|
93
|
+
def _generate_assertion(updater:, mods:, note: '')
|
94
|
+
return nil if updater.nil? || !mod.is_a?(Hash)
|
95
|
+
|
96
|
+
JSON.parse({
|
97
|
+
id: SecureRandom.hex(4).upcase,
|
98
|
+
provenance: updater.gsub('PROVENANCE#', ''),
|
99
|
+
timestamp: Time.now.iso8601,
|
100
|
+
status: 'new',
|
101
|
+
note: note,
|
102
|
+
assertions: mods
|
103
|
+
}.to_json)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
data/lib/uc3-dmp-id/creator.rb
CHANGED
@@ -77,8 +77,7 @@ module Uc3DmpId
|
|
77
77
|
end
|
78
78
|
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
79
79
|
|
80
|
-
# Once the DMP has been created, we need to register it's DMP ID
|
81
|
-
# PDF if applicable
|
80
|
+
# Once the DMP has been created, we need to register it's DMP ID
|
82
81
|
# -------------------------------------------------------------------------
|
83
82
|
def _post_process(json:, logger: nil)
|
84
83
|
return false unless json.is_a?(Hash)
|
data/lib/uc3-dmp-id/helper.rb
CHANGED
@@ -126,7 +126,7 @@ module Uc3DmpId
|
|
126
126
|
b = deep_copy_dmp(obj: dmp_b)
|
127
127
|
|
128
128
|
# ignore some of the attributes before comparing
|
129
|
-
%w[SK dmphub_modification_day dmphub_updated_at dmphub_created_at].each do |key|
|
129
|
+
%w[SK dmphub_modification_day dmphub_updated_at dmphub_created_at dmphub_assertions].each do |key|
|
130
130
|
a['dmp'].delete(key) unless a['dmp'][key].nil?
|
131
131
|
b['dmp'].delete(key) unless b['dmp'][key].nil?
|
132
132
|
end
|
@@ -212,7 +212,7 @@ module Uc3DmpId
|
|
212
212
|
return json.map { |obj| cleanse_dmp_json(json: obj) }.compact if json.is_a?(Array)
|
213
213
|
|
214
214
|
cleansed = {}
|
215
|
-
allowable = %w[dmphub_versions]
|
215
|
+
allowable = %w[dmphub_versions dmphub_updated_at]
|
216
216
|
json.each_key do |key|
|
217
217
|
next if (key.to_s.start_with?('dmphub') && !allowable.include?(key)) || %w[PK SK].include?(key.to_s)
|
218
218
|
|
data/lib/uc3-dmp-id/updater.rb
CHANGED
@@ -1,53 +1,63 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'securerandom'
|
4
|
+
|
3
5
|
module Uc3DmpId
|
4
6
|
class UpdaterError < StandardError; end
|
5
7
|
|
6
8
|
class Updater
|
7
9
|
class << self
|
8
|
-
# Update a
|
10
|
+
# Update a DMP ID
|
9
11
|
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
10
12
|
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
11
13
|
# -------------------------------------------------------------------------
|
12
|
-
def update(provenance:, p_key:, json: {}, logger: nil)
|
14
|
+
def update(provenance:, p_key:, json: {}, note: nil, logger: nil)
|
13
15
|
raise UpdaterError, MSG_DMP_INVALID_DMP_ID unless p_key.is_a?(String) && !p_key.strip.empty?
|
14
16
|
|
15
|
-
|
17
|
+
mods = Helper.parse_json(json: json).fetch('dmp', {})
|
16
18
|
p_key = Helper.append_pk_prefix(p_key: p_key)
|
19
|
+
logger.debug(message: "Incoming modifications for PK #{p_key}", details: mods) if logger.respond_to?(:debug)
|
17
20
|
|
18
|
-
#
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
client = Uc3DmpDynamo::Client.new(logger: logger)
|
24
|
-
existing = Finder.by_pk(p_key: p_key, client: client, logger: logger, cleanse: false)
|
25
|
-
logger.debug(message: "Existing latest record", details: existing) if logger.respond_to?(:debug)
|
21
|
+
# Fetch the latest version of the DMP ID
|
22
|
+
client = Uc3DmpDynamo::Client.new
|
23
|
+
latest_version = Finder.by_pk(p_key: p_key, client: client, logger: logger, cleanse: false)
|
24
|
+
latest_version = latest_version.nil? ? {} : latest_version.fetch('dmp', {})
|
25
|
+
logger.debug(message: "Latest version for PK #{p_key}", details: latest_version) if logger.respond_to?(:debug)
|
26
26
|
|
27
|
-
|
27
|
+
# Verify that the DMP ID is updateable with the info passed in
|
28
|
+
errs = _updateable?(provenance: provenance, p_key: p_key, latest_version: latest_version['dmp'], mods: mods['dmp'])
|
29
|
+
logger.error(message: errs.join(', ')) if errs.is_a?(Array) && errs.any?
|
28
30
|
raise UpdaterError, errs if errs.is_a?(Array) && errs.any?
|
29
31
|
# Don't continue if nothing has changed!
|
30
|
-
raise UpdaterError, MSG_NO_CHANGE if Helper.eql?(dmp_a:
|
32
|
+
raise UpdaterError, MSG_NO_CHANGE if Helper.eql?(dmp_a: latest_version, dmp_b: mods)
|
31
33
|
|
32
|
-
#
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
34
|
+
# Version the DMP ID record (if applicable).
|
35
|
+
owner = latest_version['dmphub_provenance_id']
|
36
|
+
updater = provenance['PK']
|
37
|
+
version = Versioner.generate_version(client: client, latest_version: latest_version, owner: owner, updater: updater,
|
38
|
+
logger: logger)
|
39
|
+
raise UpdaterError, MSG_DMP_UNABLE_TO_VERSION if version.nil?
|
37
40
|
|
38
|
-
#
|
39
|
-
|
41
|
+
# Splice the assertions
|
42
|
+
version = _process_modifications(owner: owner, updater: updater, version: version, mods: mods, note: note,
|
43
|
+
logger: logger)
|
44
|
+
|
45
|
+
# Save the changes
|
46
|
+
resp = client.put_item(json: version, logger: logger)
|
40
47
|
raise UpdaterError, MSG_DMP_UNABLE_TO_VERSION if resp.nil?
|
41
48
|
|
42
|
-
# Send the updates to EZID
|
43
|
-
_post_process(provenance: provenance, json:
|
49
|
+
# Send the updates to EZID
|
50
|
+
_post_process(provenance: provenance, json: version, logger: logger)
|
51
|
+
|
44
52
|
# Return the new version record
|
45
53
|
logger.info(message: "Updated DMP ID: #{p_key}") if logger.respond_to?(:debug)
|
46
|
-
Helper.cleanse_dmp_json(json: JSON.parse({ dmp:
|
54
|
+
Helper.cleanse_dmp_json(json: JSON.parse({ dmp: version }.to_json))
|
47
55
|
end
|
48
56
|
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
49
57
|
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
50
58
|
|
59
|
+
# Save a DMP ID's corresponding narrative PDF document to S3 and add the download URL for that
|
60
|
+
# document to the DMP ID's :dmpraodmap_related_identifiers array as an `is_metadata_for` relation
|
51
61
|
def attach_narrative(provenance:, p_key:, url:, logger: nil)
|
52
62
|
raise UpdaterError, MSG_DMP_INVALID_DMP_ID unless p_key.is_a?(String) && !p_key.strip.empty?
|
53
63
|
|
@@ -59,7 +69,7 @@ module Uc3DmpId
|
|
59
69
|
errs = _updateable?(provenance: provenance, p_key: p_key, json: dmp['dmp'])
|
60
70
|
raise UpdaterError, errs if errs.is_a?(Array) && errs.any?
|
61
71
|
|
62
|
-
# Add the
|
72
|
+
# Add the download URl for the PDF as a related identifier on the DMP ID record
|
63
73
|
annotated = Helper.annotate_dmp_json(provenance: provenance, p_key: p_key, json: dmp['dmp'])
|
64
74
|
annotated['dmproadmap_related_identifiers'] = [] if annotated['dmproadmap_related_identifiers'].nil?
|
65
75
|
annotated['dmproadmap_related_identifiers'] << {
|
@@ -76,19 +86,30 @@ module Uc3DmpId
|
|
76
86
|
|
77
87
|
private
|
78
88
|
|
79
|
-
# Check
|
80
|
-
def _updateable?(provenance:, p_key:,
|
89
|
+
# Check to make sure the incoming JSON is valid, the DMP ID requested matches the DMP ID in the JSON
|
90
|
+
def _updateable?(provenance:, p_key:, latest_version: {}, mods: {})
|
81
91
|
# Validate the incoming JSON first
|
82
|
-
errs = Validator.validate(mode: 'author', json:
|
92
|
+
errs = Validator.validate(mode: 'author', json: JSON.parse({ dmp: mods }.to_json))
|
83
93
|
return errs.join(', ') if errs.is_a?(Array) && errs.any? && errs.first != Validator::MSG_VALID_JSON
|
84
94
|
# Fail if the provenance is not defined
|
85
95
|
return [MSG_DMP_FORBIDDEN] unless provenance.is_a?(Hash) && !provenance['PK'].nil?
|
86
96
|
# Verify that the JSON is for the same DMP in the PK
|
87
|
-
|
88
|
-
|
97
|
+
return [MSG_DMP_FORBIDDEN] unless Helper.dmp_id_to_pk(json: mods.fetch('dmp_id', {})) == p_key
|
98
|
+
# Bail out if the DMP ID could not be found or the PKs do not match for some reason
|
99
|
+
return [MSG_DMP_UNKNOWN] if latest_version.nil? || latest_version.fetch['PK'] != p_key
|
100
|
+
end
|
101
|
+
|
102
|
+
def _process_modifications(owner:, updater:, version:, mods:, note: nil, logger: nil)
|
103
|
+
return version unless mods.is_a?(Hash) && !updater.nil?
|
104
|
+
return mods unless version.is_a?(Hash) && !owner.nil?
|
105
|
+
|
106
|
+
# Splice together any assertions that may have been made while the user was editing the DMP ID
|
107
|
+
Asserter.splice(latest_version: version, modified_version: mods, logger: logger) if owner == updater
|
108
|
+
# Attach the incoming changes as an assertion to the DMP ID since the updater is NOT the owner
|
109
|
+
Asserter.add(updater: provenance['PK'], dmp: version, mods: mods, note: note, logger: logger) if owner != updater
|
89
110
|
end
|
90
111
|
|
91
|
-
# Once the DMP has been updated, we need to
|
112
|
+
# Once the DMP has been updated, we need to update it's DOI metadata
|
92
113
|
# -------------------------------------------------------------------------
|
93
114
|
def _post_process(provenance:, json:, logger: nil)
|
94
115
|
return false unless json.is_a?(Hash)
|
data/lib/uc3-dmp-id/version.rb
CHANGED
data/lib/uc3-dmp-id/versioner.rb
CHANGED
@@ -26,39 +26,42 @@ module Uc3DmpId
|
|
26
26
|
client.query(args: args, logger: logger)
|
27
27
|
end
|
28
28
|
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
client = Uc3DmpDynamo::Client.new if client.nil?
|
42
|
-
|
43
|
-
owner = latest_version['dmphub_provenance_id']
|
44
|
-
updater = provenance['PK']
|
45
|
-
prior = _generate_version(client: client, latest_version: latest_version['dmp'], owner: owner, updater: updater)
|
46
|
-
return nil if prior.nil?
|
29
|
+
# Generate a snapshot of the current latest version of the DMP ID using the existing :dmphub_updated_at as
|
30
|
+
# the new SK.
|
31
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
32
|
+
def generate_version(client:, latest_version:, owner:, updater:, logger: nil)
|
33
|
+
# Only create a version if the Updater is not the Owner OR the changes have happened on a different day
|
34
|
+
mod_time = Time.parse(latest_version.fetch('dmphub_updated_at', Time.now.iso8601))
|
35
|
+
now = Time.now
|
36
|
+
if mod_time.nil? || !(now - mod_time).is_a?(Float)
|
37
|
+
logger.error(message: "#{SOURCE} unable to determine mod time: #{mod_time}") if logger.respond_to?(:debug)
|
38
|
+
return latest_version
|
39
|
+
end
|
47
40
|
|
48
|
-
|
49
|
-
|
41
|
+
# Only allow a new version if the owner and updater are the same and it has been at least one hour since
|
42
|
+
# the last version was created
|
43
|
+
same_hour = (now - mod_time).round <= 3600
|
44
|
+
if owner != updater || (owner == updater && same_hour)
|
45
|
+
logger.debug(message: "#{SOURCE} same owner and updater? #{owner == updater}") if logger.respond_to?(:debug)
|
46
|
+
logger.debug(message: "#{SOURCE} already updated within the past hour? #{same_hour}") if logger.respond_to?(:debug)
|
47
|
+
return latest_version
|
48
|
+
end
|
50
49
|
|
51
|
-
#
|
52
|
-
#
|
53
|
-
|
54
|
-
|
55
|
-
#
|
56
|
-
|
57
|
-
|
50
|
+
# Make a copy of the latest_version and then update it's SK to the :dmphub_updated_at to mark it in a point of time
|
51
|
+
# We essentially make a snapshot of the record before making changes
|
52
|
+
prior = Helper.deep_copy_dmp(obj: latest_version)
|
53
|
+
prior['SK'] = "#{Helper::SK_DMP_PREFIX}#{latest_version['dmphub_updated_at'] || Time.now.iso8601}"
|
54
|
+
# Create the prior version record ()
|
55
|
+
client = client.nil? ? Uc3DmpDynamo::Client.new : client
|
56
|
+
resp = client.put_item(json: prior, logger: logger)
|
57
|
+
return nil if resp.nil?
|
58
58
|
|
59
|
-
|
60
|
-
|
59
|
+
msg = "#{SOURCE} created version PK: #{prior['PK']} SK: #{prior['SK']}"
|
60
|
+
logger.info(message: msg, details: prior) if logger.respond_to?(:debug)
|
61
|
+
# Return the latest version
|
62
|
+
latest_version
|
61
63
|
end
|
64
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
62
65
|
|
63
66
|
# Build the :dmphub_versions array and attach it to the DMP JSON
|
64
67
|
# rubocop:disable Metrics/AbcSize
|
@@ -80,44 +83,6 @@ puts dmp
|
|
80
83
|
json
|
81
84
|
end
|
82
85
|
# rubocop:enable Metrics/AbcSize
|
83
|
-
|
84
|
-
private
|
85
|
-
|
86
|
-
# Determine whether the specified DMP metadata is versionable - returns boolean
|
87
|
-
def _versionable?(p_key:, dmp:)
|
88
|
-
|
89
|
-
puts "PK MATCH? #{dmp['PK'] == p_key}, SK IS LATEST? #{dmp['SK'] == Helper::DMP_LATEST_VERSION}, DMP ID PRESENT? #{!dmp.fetch('dmp_id', {})['identifier'].nil?}"
|
90
|
-
|
91
|
-
return false unless dmp.is_a?(Hash) && dmp['PK'] == p_key && dmp['SK'] == Helper::DMP_LATEST_VERSION
|
92
|
-
|
93
|
-
# It's versionable if it has a DMP ID
|
94
|
-
!dmp.fetch('dmp_id', {})['identifier'].nil?
|
95
|
-
end
|
96
|
-
|
97
|
-
# Generate a version
|
98
|
-
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
99
|
-
def _generate_version(client:, latest_version:, owner:, updater:, logger: nil)
|
100
|
-
# Only create a version if the Updater is not the Owner OR the changes have happened on a different day
|
101
|
-
mod_time = Time.parse(latest_version.fetch('dmphub_updated_at', Time.now.iso8601))
|
102
|
-
now = Time.now
|
103
|
-
logger.debug(message: "#{SOURCE} generating version - mod_time: #{mod_time}") if logger.respond_to?(:debug)
|
104
|
-
return latest_version if mod_time.nil? || !(now - mod_time).is_a?(Float)
|
105
|
-
|
106
|
-
same_hour = (now - mod_time).round <= 3600
|
107
|
-
logger.debug(message: "#{SOURCE} same owner and updater? #{owner == updater}") if logger.respond_to?(:debug)
|
108
|
-
logger.debug(message: "#{SOURCE} already updated within the past hour? #{same_hour}") if logger.respond_to?(:debug)
|
109
|
-
return latest_version if owner != updater || (owner == updater && same_hour)
|
110
|
-
|
111
|
-
latest_version['SK'] = "#{Helper::SK_DMP_PREFIX}#{latest_version['dmphub_updated_at'] || Time.now.iso8601}"
|
112
|
-
# Create the prior version record
|
113
|
-
resp = client.put_item(json: latest_version, logger: logger)
|
114
|
-
return nil if resp.nil?
|
115
|
-
|
116
|
-
msg = "#{SOURCE} created version PK: #{latest_version['PK']} SK: #{latest_version['SK']}"
|
117
|
-
logger.info(message: msg, details: latest_version) if logger.respond_to?(:debug)
|
118
|
-
latest_version
|
119
|
-
end
|
120
|
-
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
121
86
|
end
|
122
87
|
end
|
123
88
|
end
|
data/lib/uc3-dmp-id.rb
CHANGED
@@ -6,11 +6,11 @@ require 'json-schema'
|
|
6
6
|
|
7
7
|
require 'uc3-dmp-event-bridge'
|
8
8
|
|
9
|
+
require 'uc3-dmp-id/asserter'
|
9
10
|
require 'uc3-dmp-id/creator'
|
10
11
|
require 'uc3-dmp-id/deleter'
|
11
12
|
require 'uc3-dmp-id/finder'
|
12
13
|
require 'uc3-dmp-id/helper'
|
13
|
-
require 'uc3-dmp-id/splicer'
|
14
14
|
require 'uc3-dmp-id/updater'
|
15
15
|
require 'uc3-dmp-id/validator'
|
16
16
|
require 'uc3-dmp-id/versioner'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: uc3-dmp-id
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.99
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Riley
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-07-
|
11
|
+
date: 2023-07-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -131,13 +131,13 @@ extra_rdoc_files: []
|
|
131
131
|
files:
|
132
132
|
- README.md
|
133
133
|
- lib/uc3-dmp-id.rb
|
134
|
+
- lib/uc3-dmp-id/asserter.rb
|
134
135
|
- lib/uc3-dmp-id/creator.rb
|
135
136
|
- lib/uc3-dmp-id/deleter.rb
|
136
137
|
- lib/uc3-dmp-id/finder.rb
|
137
138
|
- lib/uc3-dmp-id/helper.rb
|
138
139
|
- lib/uc3-dmp-id/schemas/amend.rb
|
139
140
|
- lib/uc3-dmp-id/schemas/author.rb
|
140
|
-
- lib/uc3-dmp-id/splicer.rb
|
141
141
|
- lib/uc3-dmp-id/updater.rb
|
142
142
|
- lib/uc3-dmp-id/validator.rb
|
143
143
|
- lib/uc3-dmp-id/version.rb
|
data/lib/uc3-dmp-id/splicer.rb
DELETED
@@ -1,146 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Uc3DmpId
|
4
|
-
class SplicerError < StandardError; end
|
5
|
-
|
6
|
-
class Splicer
|
7
|
-
class << self
|
8
|
-
# Splice changes from other systems onto the system of provenance's updated record
|
9
|
-
# --------------------------------------------------------------
|
10
|
-
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
11
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
12
|
-
def splice_for_owner(owner:, updater:, base:, mods:, logger: nil)
|
13
|
-
return base if owner.nil? || updater.nil? || mods.nil?
|
14
|
-
return mods if base.nil?
|
15
|
-
|
16
|
-
provenance_regex = /"dmphub_provenance_id":"#{Helper::PK_PROVENANCE_PREFIX}[a-zA-Z\-_]+"/
|
17
|
-
others = base.to_json.match(provenance_regex)
|
18
|
-
# Just return it as is if there are no mods by other systems
|
19
|
-
return mods if others.nil?
|
20
|
-
|
21
|
-
spliced = Helper.deep_copy_dmp(obj: base)
|
22
|
-
cloned_mods = Helper.deep_copy_dmp(obj: mods)
|
23
|
-
|
24
|
-
# ensure that the :project and :funding are defined
|
25
|
-
spliced['project'] = [{}] if spliced['project'].nil? || spliced['project'].empty?
|
26
|
-
spliced['project'].first['funding'] = [] if spliced['project'].first['funding'].nil?
|
27
|
-
# get all the new funding and retain other system's funding metadata
|
28
|
-
mod_fundings = cloned_mods.fetch('project', [{}]).first.fetch('funding', [])
|
29
|
-
other_fundings = spliced['project'].first['funding'].reject { |fund| fund['dmphub_provenance_id'].nil? }
|
30
|
-
# process funding (just attach all funding not owned by the system of provenance)
|
31
|
-
spliced['project'].first['funding'] = mod_fundings
|
32
|
-
spliced['project'].first['funding'] << other_fundings if other_fundings.any?
|
33
|
-
return spliced if cloned_mods['dmproadmap_related_identifiers'].nil?
|
34
|
-
|
35
|
-
# process related_identifiers (just attach all related identifiers not owned by the system of provenance)
|
36
|
-
spliced['dmproadmap_related_identifiers'] = [] if spliced['dmproadmap_related_identifiers'].nil?
|
37
|
-
mod_relateds = cloned_mods.fetch('dmproadmap_related_identifiers', [])
|
38
|
-
other_relateds = spliced['dmproadmap_related_identifiers'].reject { |id| id['dmphub_provenance_id'].nil? }
|
39
|
-
spliced['dmproadmap_related_identifiers'] = mod_relateds
|
40
|
-
spliced['dmproadmap_related_identifiers'] << other_relateds if other_relateds.any?
|
41
|
-
|
42
|
-
logger.debug(message: "JSON after splicing in changes from provenance", details: spliced) if logger.respond_to?(:debug)
|
43
|
-
spliced
|
44
|
-
end
|
45
|
-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
46
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
47
|
-
|
48
|
-
# Splice changes from the other system onto the system of provenance and other system's changes
|
49
|
-
# --------------------------------------------------------------
|
50
|
-
# rubocop:disable Metrics/AbcSize
|
51
|
-
def splice_for_others(owner:, updater:, base:, mods:, logger: nil)
|
52
|
-
return base if owner.nil? || updater.nil? || base.nil? || mods.nil?
|
53
|
-
|
54
|
-
spliced = Helper.deep_copy_dmp(obj: base)
|
55
|
-
base_funds = spliced.fetch('project', [{}]).first.fetch('funding', [])
|
56
|
-
base_relateds = spliced.fetch('dmproadmap_related_identifiers', [])
|
57
|
-
|
58
|
-
mod_funds = mods.fetch('project', [{}]).first.fetch('funding', [])
|
59
|
-
mod_relateds = mods.fetch('dmproadmap_related_identifiers', [])
|
60
|
-
|
61
|
-
# process funding
|
62
|
-
spliced['project'].first['funding'] = _update_funding(
|
63
|
-
updater: updater, base: base_funds, mods: mod_funds
|
64
|
-
)
|
65
|
-
return spliced if mod_relateds.empty?
|
66
|
-
|
67
|
-
# process related_identifiers
|
68
|
-
spliced['dmproadmap_related_identifiers'] = _update_related_identifiers(
|
69
|
-
updater: updater, base: base_relateds, mods: mod_relateds
|
70
|
-
)
|
71
|
-
|
72
|
-
logger.debug(message: "JSON after splicing in changes from non-provenance", details: spliced) if logger.respond_to?(:debug)
|
73
|
-
spliced
|
74
|
-
end
|
75
|
-
# rubocop:enable Metrics/AbcSize
|
76
|
-
|
77
|
-
private
|
78
|
-
|
79
|
-
# These Splicing operations could probably be refined or genericized to traverse the Hash
|
80
|
-
# and apply to each object
|
81
|
-
|
82
|
-
# Splice funding changes
|
83
|
-
# --------------------------------------------------------------
|
84
|
-
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
85
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
86
|
-
def _update_funding(updater:, base:, mods:)
|
87
|
-
return base if updater.nil? || mods.nil? || mods.empty?
|
88
|
-
|
89
|
-
spliced = Helper.deep_copy_dmp(obj: base)
|
90
|
-
mods.each do |funding|
|
91
|
-
# Ignore it if it has no status or grant id
|
92
|
-
next if funding['funding_status'].nil? && funding['grant_id'].nil?
|
93
|
-
|
94
|
-
# See if there is an existing funding record for the funder that's waiting on an update
|
95
|
-
spliced = [] if spliced.nil?
|
96
|
-
items = spliced.select do |orig|
|
97
|
-
!orig['funder_id'].nil? &&
|
98
|
-
orig['funder_id'] == funding['funder_id'] &&
|
99
|
-
%w[applied planned].include?(orig['funding_status'])
|
100
|
-
end
|
101
|
-
# Always grab the most current
|
102
|
-
items = items.sort { |a, b| b.fetch('dmphub_created_at', '') <=> a.fetch('dmphub_created_at', '') }
|
103
|
-
item = items.first
|
104
|
-
|
105
|
-
# Out with the old and in with the new
|
106
|
-
spliced.delete(item) unless item.nil?
|
107
|
-
# retain the original name
|
108
|
-
funding['name'] = item['name'] unless item.nil?
|
109
|
-
item = Helper.deep_copy_dmp(obj: funding)
|
110
|
-
|
111
|
-
item['funding_status'] == funding['funding_status'] unless funding['funding_status'].nil?
|
112
|
-
spliced << item if funding['grant_id'].nil?
|
113
|
-
next if funding['grant_id'].nil?
|
114
|
-
|
115
|
-
item['grant_id'] = funding['grant_id']
|
116
|
-
item['funding_status'] = funding['grant_id'].nil? ? 'rejected' : 'granted'
|
117
|
-
|
118
|
-
# Add the provenance to the entry
|
119
|
-
item['grant_id']['dmphub_provenance_id'] = updater
|
120
|
-
item['grant_id']['dmphub_created_at'] = Time.now.iso8601
|
121
|
-
spliced << item
|
122
|
-
end
|
123
|
-
spliced
|
124
|
-
end
|
125
|
-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
126
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
127
|
-
|
128
|
-
# Splice related identifier changes
|
129
|
-
# --------------------------------------------------------------
|
130
|
-
def _update_related_identifiers(updater:, base:, mods:)
|
131
|
-
return base if updater.nil? || mods.nil? || mods.empty?
|
132
|
-
|
133
|
-
# Remove the updater's existing related identifiers and replace with the new set
|
134
|
-
spliced = base.nil? ? [] : Helper.deep_copy_dmp(obj: base)
|
135
|
-
spliced = spliced.reject { |related| related['dmphub_provenance_id'] == updater }
|
136
|
-
# Add the provenance to the entry
|
137
|
-
updates = mods.nil? ? [] : Helper.deep_copy_dmp(obj: mods)
|
138
|
-
updates = updates.map do |related|
|
139
|
-
related['dmphub_provenance_id'] = updater
|
140
|
-
related
|
141
|
-
end
|
142
|
-
spliced + updates
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|