Skip to content

Commit e18fd9f

Browse files
authored
Adding the ability to call to mediaflux to push an update for the submission event (#738)
Unfortunately we have to replace the entire document we can not just replace single elements.
1 parent c79a8e3 commit e18fd9f

File tree

7 files changed

+249
-4
lines changed

7 files changed

+249
-4
lines changed
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
# frozen_string_literal: true
2+
module Mediaflux
3+
module Http
4+
# Constructs a request to mediaflux to approve a project
5+
#
6+
# @example
7+
# project = Project.first
8+
# project.save_in_mediaflux(session_id: User.first.mediaflux_session)
9+
# approve_req = Mediaflux::Http::AssetApproveRequest.new(session_token: User.first.mediaflux_session, project:)
10+
# approve_req.resolve
11+
#
12+
class AssetApproveRequest < Request
13+
attr_reader :project_metadata, :project
14+
# Constructor
15+
# @param session_token [String] the API token for the authenticated session
16+
# @param project [Project] project to approve
17+
# @param xml_namespace [String] XML namespace for the <project> element
18+
def initialize(session_token:, project:, xml_namespace: nil, xml_namespace_uri: nil)
19+
super(session_token: session_token)
20+
@project = project
21+
@project_metadata = project.metadata
22+
@xml_namespace = xml_namespace || self.class.default_xml_namespace
23+
@xml_namespace_uri = xml_namespace_uri || self.class.default_xml_namespace_uri
24+
end
25+
26+
# Specifies the Mediaflux service to use when updating assets
27+
# @return [String]
28+
def self.service
29+
"asset.set"
30+
end
31+
32+
private
33+
34+
# The generated XML mimics what we get when we issue an Aterm command as follows:
35+
# service.execute :service -name "asset.set" \
36+
# < :id "1574" :meta -action "replace" < :tigerdata:project -xmlns:tigerdata "tigerdata" < \
37+
# :ProjectDirectory "/td-demo-001/tigerdataNS/test-05-30-24" :Title "testing approval" :Description "I want to test the approval updates" \
38+
# :Status "approved" :DataSponsor "cac9" :DataManager "mjc12" :Department "RDSS" :DataUser -ReadOnly "true" "la15" :DataUser "woongkim" \
39+
# :CreatedOn "30-MAY-2024 09:11:09" :CreatedBy "cac9" :ProjectID "10.34770/tbd"
40+
# :StorageCapacity < :Size -Requested "500" -Approved "1" "1" :Unit -Requested "GB" -Approved "TB" "TB" > \
41+
# :Performance -Requested "Standard" -Approved "Standard" "Standard" :Submission < :RequestedBy "cac9" \
42+
# :RequestDateTime "30-MAY-2024 13:11:09" :ApprovedBy "cac9" :ApprovalDateTime "30-MAY-2024 13:12:44" \
43+
# :EventlNote < :NoteDateTime "30-MAY-2024 13:12:44" :NoteBy "cac9" :EventType "Quota" :Message "A note"\
44+
# > > :ProjectPurpose "Research" :SchemaVersion "0.6.1" > > >
45+
#
46+
def build_http_request_body(name:)
47+
super do |xml|
48+
xml.args do
49+
xml.id project.mediaflux_id
50+
xml.meta do
51+
xml.parent.set_attribute("action", "replace")
52+
doc = xml.doc
53+
root = doc.root
54+
# Define the namespace only if this is required
55+
root.add_namespace_definition(@xml_namespace, @xml_namespace_uri)
56+
57+
element_name = @xml_namespace.nil? ? "project" : "#{@xml_namespace}:project"
58+
xml.send(element_name) do
59+
build_project(xml)
60+
end
61+
end
62+
end
63+
end
64+
end
65+
66+
def build_project(xml)
67+
build_basic_project_meta(xml)
68+
build_departments(xml, project_metadata[:departments])
69+
build_read_only_user(xml, project_metadata[:data_user_read_only])
70+
build_read_write_users(xml, project_metadata[:data_user_read_write])
71+
xml.CreatedOn self.class.format_date_for_mediaflux(project_metadata[:created_on])
72+
xml.CreatedBy project_metadata[:created_by]
73+
xml.ProjectID project_metadata[:project_id]
74+
build_storage_capacity(xml)
75+
build_performance(xml)
76+
build_submission(xml)
77+
xml.ProjectPurpose project_metadata[:project_purpose]
78+
xml.SchemaVersion TigerdataSchema::SCHEMA_VERSION
79+
end
80+
81+
def build_basic_project_meta(xml)
82+
xml.ProjectDirectory project_metadata[:project_directory]
83+
xml.Title project_metadata[:title]
84+
xml.Description project_metadata[:description] if project_metadata[:description].present?
85+
xml.Status project_metadata[:status]
86+
xml.DataSponsor project_metadata[:data_sponsor]
87+
xml.DataManager project_metadata[:data_manager]
88+
end
89+
90+
def build_departments(xml, departments)
91+
return if departments.blank?
92+
93+
departments.each do |department|
94+
xml.Department department
95+
end
96+
end
97+
98+
def build_read_only_user(xml, ro_users)
99+
return if ro_users.blank?
100+
101+
ro_users.each do |ro_user|
102+
xml.DataUser do
103+
xml.parent.set_attribute("ReadOnly", true)
104+
xml.text(ro_user)
105+
end
106+
end
107+
end
108+
109+
def build_read_write_users(xml, rw_users)
110+
return if rw_users.blank?
111+
112+
rw_users.each do |rw_user|
113+
xml.DataUser rw_user
114+
end
115+
end
116+
117+
def build_storage_capacity(xml)
118+
xml.StorageCapacity do
119+
xml.Size do
120+
build_value(xml, project_metadata[:storage_capacity][:size][:requested], project_metadata[:storage_capacity][:size][:approved])
121+
end
122+
xml.Unit do
123+
build_value(xml, project_metadata[:storage_capacity][:unit][:requested], project_metadata[:storage_capacity][:unit][:approved])
124+
end
125+
end
126+
end
127+
128+
def build_performance(xml)
129+
xml.Performance do
130+
build_value(xml, project_metadata[:storage_performance_expectations][:requested], project_metadata[:storage_performance_expectations][:approved])
131+
end
132+
end
133+
134+
def build_value(xml, requested, approved)
135+
xml.parent.set_attribute("Requested", requested)
136+
xml.parent.set_attribute("Approved", approved)
137+
xml.text(approved)
138+
end
139+
140+
def build_submission(xml)
141+
xml.Submission do
142+
xml.RequestedBy submission_event.event_person
143+
xml.RequestDateTime self.class.format_date_for_mediaflux(submission_event.created_at.iso8601)
144+
xml.ApprovedBy approval_event.event_person
145+
xml.ApprovalDateTime self.class.format_date_for_mediaflux(approval_event.created_at.iso8601)
146+
build_submission_note(xml)
147+
end
148+
end
149+
150+
def build_submission_note(xml)
151+
return if approval_event.event_note.blank?
152+
153+
xml.EventlNote do
154+
xml.NoteDateTime self.class.format_date_for_mediaflux(approval_event.created_at.iso8601)
155+
xml.NoteBy approval_event.event_note["note_by"]
156+
xml.EventType approval_event.event_note["event_type"]
157+
xml.Message approval_event.event_note["message"]
158+
end
159+
end
160+
161+
def approval_event
162+
@approval_event ||= project.provenance_events.find_by(event_type: ProvenanceEvent::APPROVAL_EVENT_TYPE)
163+
end
164+
165+
def submission_event
166+
@submission_event ||= project.provenance_events.find_by(event_type: ProvenanceEvent::SUBMISSION_EVENT_TYPE)
167+
end
168+
end
169+
end
170+
end

app/models/mediaflux/http/request.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,13 @@ def xtoshell_xml( name: self.class.service)
142142
xml.strip.gsub("\"","'").gsub("<args>","").gsub("</args>","")
143143
end
144144

145+
# This method is used for transforming iso8601 dates to dates that MediaFlux likes
146+
# Take a string like "2024-02-26T10:33:11-05:00" and convert this string to "22-FEB-2024 13:57:19"
147+
def self.format_date_for_mediaflux(iso8601_date)
148+
return if iso8601_date.nil?
149+
Time.parse(iso8601_date).strftime("%e-%b-%Y %H:%M:%S").upcase
150+
end
151+
145152
private
146153

147154
def http_request

app/models/project_metadata.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,16 @@ def approve_project(params:)
4343
project.metadata_json["status"] = Project::APPROVED_STATUS
4444
project.metadata_json["project_directory"] = "#{params[:project_directory_prefix]}/#{params[:project_directory]}"
4545
project.metadata_json["storage_capacity"] = params[:storage_capacity]
46+
project.metadata_json["storage_performance_expectations"] = params[:storage_performance_expectations]
4647

4748
project.save!
49+
generate_approval_events(params[:approval_note])
50+
end
51+
52+
def generate_approval_events(note)
4853
# create two provenance events, one for approving the project and another for changing the status of the project
4954
project.provenance_events.create(event_type: ProvenanceEvent::APPROVAL_EVENT_TYPE, event_person: current_user.uid, event_details: "Approved by #{current_user.display_name_safe}",
50-
event_note: params[:approval_note])
55+
event_note: note)
5156
project.provenance_events.create(event_type: ProvenanceEvent::STATUS_UPDATE_EVENT_TYPE, event_person: current_user.uid, event_details: "The Status of this project has been set to approved")
5257
end
5358

spec/factories/project.rb

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
project_purpose { "research" }
1818
project_directory { "big-data" }
1919
schema_version { ::TigerdataSchema::SCHEMA_VERSION }
20+
approved_by { nil }
21+
approved_on { nil }
2022
end
2123
mediaflux_id { nil }
2224
metadata do
@@ -38,7 +40,9 @@
3840
storage_capacity: storage_capacity,
3941
storage_performance_expectations: storage_performance,
4042
project_purpose: project_purpose,
41-
schema_version: schema_version
43+
schema_version: schema_version,
44+
approved_by: approved_by,
45+
approved_on: approved_on
4246
}
4347
end
4448
factory :project_with_doi, class: "Project" do
@@ -53,5 +57,16 @@
5357
end
5458
end
5559
end
60+
61+
factory :approved_project, class: "Project" do
62+
transient do
63+
storage_capacity { { size: { requested: 500, approved: 600 }, unit: { requested: "GB", approved: "KB" } } }
64+
storage_performance { { requested: "standard", approved: "performant" } }
65+
status { "approved" }
66+
approved_by { FactoryBot.create(:sysadmin).uid }
67+
approved_on { Time.current.in_time_zone("America/New_York").iso8601 }
68+
project_id { "10.34770/tbd" }
69+
end
70+
end
5671
end
5772
end
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# frozen_string_literal: true
2+
require "rails_helper"
3+
4+
RSpec.describe Mediaflux::Http::AssetApproveRequest, type: :model, connect_to_mediaflux: true do
5+
let(:approver) { FactoryBot.create :sysadmin }
6+
let(:session_id) { approver.mediaflux_session }
7+
let(:approved_project) do
8+
project = FactoryBot.create :approved_project
9+
mediaflux_id = project.save_in_mediaflux(session_id: )
10+
meta = ProjectMetadata.new(project: project, current_user: approver)
11+
data_sponsor = User.find_by(uid: project.metadata[:data_sponsor])
12+
project.provenance_events.create(event_type: ProvenanceEvent::SUBMISSION_EVENT_TYPE, event_person: data_sponsor.uid, event_details: "Requested by #{data_sponsor.display_name_safe}")
13+
meta.approve_project(params: { mediaflux_id:, project_directory_prefix: "tigerns/test", project_directory: "approved_project",
14+
storage_capacity: { size: { requested: 200, approved: 100 }, unit: { requested: "PB", approved: "TB" } },
15+
storage_performance_expectations: { requested: "Standard", approved: "Fast" } })
16+
project
17+
end
18+
19+
let(:approve_request_xml) do
20+
filename = Rails.root.join("spec", "fixtures", "files", "asset_approve_request.xml")
21+
File.new(filename).read
22+
end
23+
24+
let(:approve_response_xml) do
25+
filename = Rails.root.join("spec", "fixtures", "files", "asset_approve_response.xml")
26+
File.new(filename).read
27+
end
28+
29+
describe "#resolve" do
30+
it "updates the submission" do
31+
approve_request = described_class.new(session_token: session_id, project: approved_project)
32+
approve_request.resolve
33+
req = Mediaflux::Http::AssetMetadataRequest.new(session_token: session_id, id: approved_project.mediaflux_id)
34+
metadata = req.metadata
35+
expect(req.error?).to be_falsey
36+
approval = approved_project.provenance_events.find_by(event_type: ProvenanceEvent::APPROVAL_EVENT_TYPE)
37+
expect(metadata[:submission][:approved_by]).to eq(approval.event_person)
38+
submission = approved_project.provenance_events.find_by(event_type: ProvenanceEvent::SUBMISSION_EVENT_TYPE)
39+
expect(metadata[:submission][:requested_by]).to eq(submission.event_person)
40+
end
41+
end
42+
end

spec/models/project_mediaflux_spec.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@
109109
"requested"=>project.metadata[:storage_capacity][:size][:requested]},
110110
"unit"=>{"approved"=>"GB", "requested"=>"GB"}},
111111
event_note: "Other",
112-
event_note_message: "Message filler"
112+
event_note_message: "Message filler",
113+
storage_performance_expectations: { "requested" => "standard" }
113114
}
114115
project_metadata.approve_project(params:)
115116
session_token = current_user.mediaflux_session
@@ -134,7 +135,8 @@
134135
"requested"=>project.metadata[:storage_capacity][:size][:requested]},
135136
"unit"=>{"approved"=>"GB", "requested"=>"GB"}},
136137
event_note: "Other",
137-
event_note_message: "Message filler"
138+
event_note_message: "Message filler",
139+
storage_performance_expectations: { "requested" => "standard" }
138140
}
139141
project_metadata.approve_project(params:)
140142
session_token = current_user.mediaflux_session

spec/models/project_metadata_spec.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@
152152
storage_capacity: {"size"=>{"approved"=>600,
153153
"requested"=>project.metadata[:storage_capacity][:size][:requested]},
154154
"unit"=>{"approved"=>"GB", "requested"=>"GB"}},
155+
storage_performance_expectations: { requested: "Standard", approved: "Fast" },
155156
event_note: "Other",
156157
event_note_message: "Message filler"
157158
}
@@ -169,6 +170,7 @@
169170
storage_capacity: {"size"=>{"approved"=>600,
170171
"requested"=>project.metadata[:storage_capacity][:size][:requested]},
171172
"unit"=>{"approved"=>"GB", "requested"=>"GB"}},
173+
storage_performance_expectations: { requested: "Standard", approved: "Fast" },
172174
event_note: "Other",
173175
event_note_message: "Message filler"
174176
}
@@ -194,6 +196,7 @@
194196
storage_capacity: {"size"=>{"approved"=>600,
195197
"requested"=>project.metadata[:storage_capacity][:size][:requested]},
196198
"unit"=>{"approved"=>"GB", "requested"=>"GB"}},
199+
storage_performance_expectations: { requested: "Standard", approved: "Fast" },
197200
event_note: "Other",
198201
event_note_message: "Message filler"
199202
}
@@ -244,6 +247,7 @@
244247
storage_capacity: {"size"=>{"approved"=>600,
245248
"requested"=>project.metadata[:storage_capacity][:size][:requested]},
246249
"unit"=>{"approved"=>"GB", "requested"=>"GB"}},
250+
storage_performance_expectations: { requested: "Standard", approved: "Fast" },
247251
event_note: "Other",
248252
event_note_message: "Message filler"
249253
}

0 commit comments

Comments
 (0)