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

pulibrary / tigerdata-app / 6b20c09e-9eaa-41d9-80ef-e6ce0d0036a9

07 Nov 2025 06:12PM UTC coverage: 87.615% (-3.7%) from 91.357%
6b20c09e-9eaa-41d9-80ef-e6ce0d0036a9

Pull #2163

circleci

web-flow
Merge branch 'main' into 2157-project-list-error
Pull Request #2163: Logs Mediaflux errors when fetching the project list

8 of 8 new or added lines in 1 file covered. (100.0%)

523 existing lines in 31 files now uncovered.

2752 of 3141 relevant lines covered (87.62%)

345.77 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✔
UNCOV
26
    check_errors? do
45✔
UNCOV
27
      field_present?(project_title, :project_title)
45✔
UNCOV
28
      valid_length(project_title, 200, :project_title)
45✔
UNCOV
29
      no_quotes(project_title, :project_title)
45✔
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✔
UNCOV
58
    check_errors? do
2✔
UNCOV
59
      field_present?(parent_folder, :parent_folder)
2✔
UNCOV
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✔
UNCOV
73
       (custom_quota? && (storage_size.present? && (storage_size > 0)) && ((storage_unit == "GB") || (storage_unit == "TB")))
4✔
UNCOV
74
      true
41✔
75
    else
UNCOV
76
      errors.add(:quota, :invalid, message: "must be one of '500 GB', '2 TB', '10 TB', '25 TB', or 'custom'")
3✔
UNCOV
77
      false
3✔
78
    end
79
  end
80

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

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

89
  def approve(approver)
4✔
90
    create_project_operation = ProjectCreate.new
179✔
91
    result = create_project_operation.call(request: self, approver: approver)
179✔
92
    if result.success?
179✔
93
      result.value!
176✔
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?
190✔
104
      if approved_quota == "custom"
173✔
105
        approved_storage_size.to_f
172✔
106
      else
UNCOV
107
        approved_quota.split.first.to_f
1✔
108
      end
109
    else
UNCOV
110
      requested_quota_size
17✔
111
    end
112
  end
113

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

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

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

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

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

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

152
  def requestor
4✔
UNCOV
153
    return "missing" if requested_by.blank?
1✔
UNCOV
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✔
UNCOV
178
      original_error_count = errors.count
281✔
UNCOV
179
      yield
281✔
UNCOV
180
      original_error_count == errors.count
281✔
181
    end
182

183
    def field_present?(value, name)
4✔
UNCOV
184
      if value.blank?
167✔
UNCOV
185
        errors.add(name, :invalid, message: "cannot be empty")
52✔
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✔
UNCOV
192
      elsif User.where(uid: uid).count == 0
59✔
UNCOV
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✔
UNCOV
204
      return if value.blank?
86✔
UNCOV
205
      if value.length > length
62✔
UNCOV
206
        errors.add(field, :invalid, message: "cannot exceed #{length} characters")
4✔
207
      end
208
    end
209

210
    def no_quotes(value, field)
4✔
UNCOV
211
      return if value.blank?
127✔
UNCOV
212
      if value.include?('"')
87✔
UNCOV
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✔
UNCOV
223
        Rails.logger.warn("Deleting project #{project.id} because the approval for request #{id} failed and it was not created in Mediaflux.")
2✔
UNCOV
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