uc3-dmp-id 0.0.96 → 0.0.98
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/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 +53 -32
- 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: 6e67c682d15718449d9e74b2a63d99a25d702b26530728073df044ab40b8a743
|
4
|
+
data.tar.gz: 6e8f4859645c076262d68dd351eb9c27b978932b238734ecfa02c608b5161966
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc9e07e81387893b912a62d1e68a28aa35cbc6f25e8d571cd2c05b78780454b40f72cf779e57b2f6b8908ad6df25c81de2fe01f447fd1ca6945801984959f2a4
|
7
|
+
data.tar.gz: 5dc220e48c1c46e719e7f6345a2f3712248a0ccbc1591e7eb5dcf09908ab49c78b4bec9b8de5f2807947cdbb02db8272aec4d5aae4bb7420b144d5accb2f51f6
|
@@ -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, 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(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,25 +86,36 @@ 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
|
-
def _post_process(json:, logger: nil)
|
114
|
+
def _post_process(provenance:, json:, logger: nil)
|
94
115
|
return false unless json.is_a?(Hash)
|
95
116
|
|
96
117
|
# Indicate whether or not the updater is the provenance system
|
97
|
-
json['dmphub_updater_is_provenance'] =
|
118
|
+
json['dmphub_updater_is_provenance'] = provenance['PK'] == json['dmphub_provenance_id']
|
98
119
|
# Publish the change to the EventBridge
|
99
120
|
publisher = Uc3DmpEventBridge::Publisher.new
|
100
121
|
publisher.publish(source: 'DmpUpdater', dmp: json, logger: logger)
|
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.98
|
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
|