• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pulibrary / tigerdata-app / 7291b10e-eaa3-4284-9371-5a980ceebf59

24 Nov 2025 07:18PM UTC coverage: 87.613% (-3.7%) from 91.333%
7291b10e-eaa3-4284-9371-5a980ceebf59

push

circleci

web-flow
Adds breadcrumb to Wizard (#2231)

Adds the breadcrumb to the Wizard and the functionality to allow the
user to save their changes before leaving the Wizard when clicking on
the "Dashboard" link in the breadcrumbs.

Closes #2102

5 of 12 new or added lines in 11 files covered. (41.67%)

904 existing lines in 36 files now uncovered.

2801 of 3197 relevant lines covered (87.61%)

360.23 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/app/models/request.rb
1
# frozen_string_literal: true
2
# rubocop:disable Metrics/ClassLength
3
class Request < ApplicationRecord
4✔
4
  DRAFT = "draft" # default state set by database
4✔
5
  SUBMITTED = "submitted" # Ready to be approved
4✔
6

7
  def valid_to_submit?
4✔
UNCOV
8
    errors.clear
36✔
9
    # run all validations and then check for errors otherwise ruby stops at the first error
UNCOV
10
    valid_title?
36✔
UNCOV
11
    valid_data_sponsor?
36✔
UNCOV
12
    valid_data_manager?
36✔
UNCOV
13
    valid_departments?
36✔
UNCOV
14
    valid_quota?
36✔
UNCOV
15
    valid_project_purpose?
36✔
UNCOV
16
    valid_description?
36✔
17
    # Is parent folder really required?  For Skeletor let's skip it.
18
    # valid_parent_folder?
UNCOV
19
    valid_project_folder?
36✔
20
    # For Skeletor we are setting the requestor to the data sponsor
21
    # valid_requested_by?
UNCOV
22
    errors.count == 0
36✔
23
  end
24

25
  def valid_title?
4✔
26
    check_errors? do
57✔
27
      field_present?(project_title, :project_title)
57✔
28
      valid_length(project_title, 200, :project_title)
57✔
29
      no_quotes(project_title, :project_title)
57✔
30
    end
31
  end
32

33
  def valid_data_sponsor?
4✔
UNCOV
34
    check_errors? { validate_uid(data_sponsor, :data_sponsor) }
78✔
35
  end
36

37
  def valid_data_manager?
4✔
UNCOV
38
    check_errors? { validate_uid(data_manager, :data_manager) }
78✔
39
  end
40

41
  def valid_departments?
4✔
UNCOV
42
    check_errors? { field_present?(departments, :departments) }
76✔
43
  end
44

45
  def valid_project_purpose?
4✔
UNCOV
46
    check_errors? { project_purpose_present?(project_purpose, :project_purpose) }
72✔
47
  end
48

49
  def valid_description?
4✔
UNCOV
50
    check_errors? do
41✔
UNCOV
51
      field_present?(description, :description)
41✔
UNCOV
52
      valid_length(description, 1000, :description)
41✔
UNCOV
53
      no_quotes(description, :description)
41✔
54
    end
55
  end
56

57
  def valid_parent_folder?
4✔
58
    check_errors? do
2✔
59
      field_present?(parent_folder, :parent_folder)
2✔
60
      no_quotes(project_title, :parent_folder)
2✔
61
    end
62
  end
63

64
  def valid_project_folder?
4✔
UNCOV
65
    check_errors? do
39✔
UNCOV
66
      field_present?(project_folder, :project_folder)
39✔
UNCOV
67
      no_quotes(project_folder, :project_folder)
39✔
68
    end
69
  end
70

71
  def valid_quota?
4✔
UNCOV
72
    if ((quota == "500 GB") || (quota == "2 TB") || (quota == "10 TB") || (quota == "25 TB")) ||
44✔
73
       (custom_quota? && (storage_size.present? && (storage_size > 0)) && ((storage_unit == "GB") || (storage_unit == "TB")))
4✔
UNCOV
74
      true
41✔
75
    else
76
      errors.add(:quota, :invalid, message: "must be one of '500 GB', '2 TB', '10 TB', '25 TB', or 'custom'")
3✔
77
      false
3✔
78
    end
79
  end
80

81
  def custom_quota?
4✔
82
    quota == "custom"
428✔
83
  end
84

85
  def valid_requested_by?
4✔
86
    check_errors? { field_present?(requested_by, :requested_by) }
4✔
87
  end
88

89
  def approve(approver)
4✔
90
    create_project_operation = ProjectCreate.new
183✔
91
    result = create_project_operation.call(request: self, approver: approver)
183✔
92
    if result.success?
183✔
93
      result.value!
180✔
94
    else
95
      self.error_message = { message: result.failure }
3✔
96
      save!
3✔
97
      cleanup_incomplete_project
3✔
98
      raise ProjectCreate::ProjectCreateError, result.failure
3✔
99
    end
100
  end
101

102
  def approved_quota_size
4✔
103
    if approved_quota.present?
194✔
104
      if approved_quota == "custom"
177✔
105
        approved_storage_size.to_f
176✔
106
      else
107
        approved_quota.split.first.to_f
1✔
108
      end
109
    else
110
      requested_quota_size
17✔
111
    end
112
  end
113

114
  def requested_quota_size
4✔
115
    if custom_quota?
212✔
116
      storage_size.to_f
180✔
117
    else
118
      quota.split.first.to_f
32✔
119
    end
120
  end
121

122
  def approved_quota_unit
4✔
123
    if approved_quota.present?
197✔
124
      if approved_quota == "custom"
179✔
125
        approved_storage_unit
178✔
126
      else
127
        approved_quota.split.last
1✔
128
      end
129
    else
130
      requested_quota_unit
18✔
131
    end
132
  end
133

134
  def requested_quota_unit
4✔
135
    if custom_quota?
212✔
136
      storage_unit
180✔
137
    else
138
      quota.split.last
32✔
139
    end
140
  end
141

142
  def submitted?
4✔
UNCOV
143
    state == Request::SUBMITTED
15✔
144
  end
145

146
  def project_path
4✔
147
    return project_folder if parent_folder.blank?
1✔
148

149
    [parent_folder, project_folder].join("/")
1✔
150
  end
151

152
  def requestor
4✔
153
    return "missing" if requested_by.blank?
1✔
154
    User.find_by(uid: requested_by).display_name_safe
1✔
155
  end
156

157
  def data_manager_name
4✔
158
    user_name(data_manager)
5✔
159
  end
160

161
  def data_sponsor_name
4✔
162
    user_name(data_sponsor)
5✔
163
  end
164

165
  private
4✔
166

167
    def user_name(uid)
4✔
168
      return "" if uid.blank?
10✔
169
      user = User.find_by(uid: uid)
2✔
170
      if user.present?
2✔
171
        user.display_name_safe
1✔
172
      else
173
        uid
1✔
174
      end
175
    end
176

177
    def check_errors?
4✔
178
      original_error_count = errors.count
293✔
179
      yield
293✔
180
      original_error_count == errors.count
293✔
181
    end
182

183
    def field_present?(value, name)
4✔
184
      if value.blank?
179✔
UNCOV
185
        errors.add(name, :invalid, message: "cannot be empty")
53✔
186
      end
187
    end
188

189
    def validate_uid(uid, field)
4✔
UNCOV
190
      if uid.blank?
78✔
UNCOV
191
        errors.add(field, :blank, message: "cannot be empty")
19✔
192
      elsif User.where(uid: uid).count == 0
59✔
193
        errors.add(field, :invalid, message: "must be a valid user")
2✔
194
      end
195
    end
196

197
    def project_purpose_present?(project_purpose, field)
4✔
UNCOV
198
      if project_purpose.blank?
36✔
UNCOV
199
        errors.add(field, :blank, message: "select a project purpose")
16✔
200
      end
201
    end
202

203
    def valid_length(value, length, field)
4✔
204
      return if value.blank?
98✔
205
      if value.length > length
73✔
206
        errors.add(field, :invalid, message: "cannot exceed #{length} characters")
4✔
207
      end
208
    end
209

210
    def no_quotes(value, field)
4✔
211
      return if value.blank?
139✔
212
      if value.include?('"')
98✔
213
        errors.add(field, :invalid, message: "cannot include quotes")
5✔
214
      end
215
    end
216

217
    # If a request fails to be a approved we make sure there were not orphan
218
    # project records left in our Rails database that do not have a matching
219
    # project in Mediaflux (i.e. collection asset).
220
    def cleanup_incomplete_project
4✔
221
      project = Project.find_by_id(project_id)
3✔
222
      if project && project.mediaflux_id.nil?
3✔
223
        Rails.logger.warn("Deleting project #{project.id} because the approval for request #{id} failed and it was not created in Mediaflux.")
2✔
224
        project.destroy!
2✔
225
      end
226
    end
227
end
228
# rubocop:enable Metrics/ClassLength
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc