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

pulibrary / tigerdata-app / 34dcbd97-a3da-420a-b11e-066310a63dda

01 Dec 2025 07:52PM UTC coverage: 87.656% (-3.7%) from 91.374%
34dcbd97-a3da-420a-b11e-066310a63dda

push

circleci

web-flow
Fixed quota display bug on dashboard (#2256)

Closes #2255 

## Local screenshot

<img width="1262" height="488" alt="Screenshot 2025-12-01 at 2 24 43 PM"
src="https://github.com/user-attachments/assets/c339863e-6d08-48f5-90c4-7a8d994fdc92"
/>

2805 of 3200 relevant lines covered (87.66%)

359.65 hits per line

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

97.78
/app/models/mediaflux/request.rb
1
# frozen_string_literal: true
2
module Mediaflux
4✔
3
    class Request
4✔
4

5
      # As this is an abstract class, this should be overridden to specify the Mediaflux API service
6
      def self.service
4✔
7
        raise(NotImplementedError, "#{self} is an abstract class, please override #{self}.service")
1✔
8
      end
9

10
      # The default request URL path for the Mediaflux API
11
      # @return [String]
12
      def self.request_path
4✔
13
        "/__mflux_svc__"
6,047✔
14
      end
15

16
      def self.uri
4✔
17
        # Setting the URI to the Ansible build or Docker build of mediaflux depending on the environment
18
        URI("#{Connection.transport}://#{Connection.host}:#{Connection.port}#{request_path}")
3,037✔
19
      end
20

21
      # Constructs a new HTTP POST request for usage with the Mediaflux API
22
      # @return [Net::HTTP::Post]
23
      def self.build_post_request
4✔
24
        Net::HTTP::Post.new(request_path)
3,010✔
25
      end
26

27
      # The default XML namespace which should be used for building the XML
28
      #   Document transmitted in the body of the HTTP request
29
      # @return [String]
30
      def self.default_xml_namespace
4✔
31
        "tigerdata"
24✔
32
      end
33

34
      def self.default_xml_namespace_uri
4✔
35
        "http://tigerdata.princeton.edu"
24✔
36
      end
37

38
      # Constructs and memoizes a new instance of the Net::HTTP::Persistent object at the level of the Class
39
      # @returns http_client [Net::HTTP::Persistent] HTTP client for transmitting requests to the Mediaflux server API
40
      def self.find_or_create_http_client
4✔
41
        HttpConnection.instance.http_client
3,017✔
42
      end
43

44
      attr_reader :session_token
4✔
45

46
      # Constructor
47
      # @param file [File] any upload file required for the POST request
48
      # @param session_token [String] the API token for the authenticated session
49
      # @param http_client [Net::HTTP::Persistent] HTTP client for transmitting requests to the Mediaflux server API
50
      def initialize(file: nil, session_token: nil, http_client: nil)
4✔
51
        @http_client = http_client || self.class.find_or_create_http_client
3,017✔
52
        @file = file
3,017✔
53
        @session_token = session_token
3,017✔
54
      end
55

56
      # Resolves the HTTP request against the Mediaflux API
57
      # @return [Net::HTTP]
58
      def resolve
4✔
59
        start_time = ::Time.zone.now
3,018✔
60
        @http_response = @http_client.request self.class.uri, http_request
3,018✔
61
        log_elapsed(start_time)
3,017✔
62
        if response_error.present?
3,017✔
63
          Rails.logger.error "Mediaflux error: #{response_error[:title]}, #{response_error[:message]}"
19✔
64
        end
65
        @http_response
3,013✔
66
      end
67

68
      # Determines whether or not the request has been resolved
69
      # @return [Boolean]
70
      def resolved?
4✔
71
        @http_response.present?
6,960✔
72
      end
73

74
      # Resolves the HTTP request, and returns the XML Document parsed from the response body
75
      # @return [Nokogiri::XML::Document]
76
      def response_xml
4✔
77
        resolve unless resolved?
6,958✔
78
        Rails.logger.debug(response_body)
6,954✔
79
        @response_xml ||= Nokogiri::XML.parse(response_body)
6,954✔
80
        Rails.logger.debug(@response_xml)
6,954✔
81
        if @response_xml.xpath("//message").text == "session is not valid"
6,954✔
82
          raise Mediaflux::SessionExpired, "Session expired for token #{session_token}"
4✔
83
        end
84

85
        @response_xml
6,950✔
86
      end
87

88
      # The response body of the mediaflux call
89
      def response_body
4✔
90
        @response_body ||= http_response.body
9,983✔
91
      end
92

93
      def error?
4✔
94
        response_error.present?
795✔
95
      end
96

97
      def response_error
4✔
98
        xml = response_xml
3,864✔
99
        return nil if xml.xpath("/response/reply/error").count == 0
3,860✔
100
        error = {
101
          title: xml.xpath("/response/reply/error").text,
79✔
102
          message: xml.xpath("/response/reply/message").text
103
        }
104
        error
79✔
105
      end
106

107
      delegate :to_s, to: :response_xml
4✔
108

109
      def xml_payload( name: self.class.service)
4✔
110
        body = build_http_request_body(name: )
6,022✔
111
        xml_payload = body.to_xml
6,022✔
112
      end
113

114
      # The output of this routine can be passed to xtoshell in aterm.  The output of which can be sent to service.execute
115
      # @param [String] name name of the service this request will send
116
      # @return [String] xml that can be passed to xtoshell without manipulation
117
      def xtoshell_xml( name: self.class.service)
4✔
118
        xml_builder = build_http_request_body(name: )
1✔
119
        xml_builder.doc.xpath("//request/service/@session").remove
1✔
120
        xml = xml_builder.to_xml(:save_with => Nokogiri::XML::Node::SaveOptions::AS_XML | Nokogiri::XML::Node::SaveOptions::NO_DECLARATION)
1✔
121
        xml.strip.gsub("\"","'").gsub("<args>","").gsub("</args>","")
1✔
122
      end
123

124
      # This method is used for transforming iso8601 dates to dates that MediaFlux likes
125
      # Take a string like "2024-02-26T10:33:11-05:00" and convert this string to "22-FEB-2024 13:57:19"
126
      def self.format_date_for_mediaflux(iso8601_date)
4✔
127
        return if iso8601_date.nil?
×
128
        Time.format_date_for_mediaflux(iso8601_date)
×
129
      end
130

131
      private
4✔
132

133
        def http_request
4✔
134
          @http_request ||= build_http_request(name: self.class.service, form_file: @file)
3,018✔
135
        end
136

137
        def http_response
4✔
138
          @http_response ||= resolve
3,018✔
139
        end
140

141
        def build_http_request_body(name:)
4✔
142
          args = { name: name }
6,023✔
143
                                                # must use @session_token here instead of session_token
144
                                                #  for login to not go into an infinaite loop
145
          args[:session] = session_token unless @session_token.nil?
6,023✔
146

147
          Nokogiri::XML::Builder.new(namespace_inheritance: false) do |xml|
6,023✔
148
            xml.request do
6,023✔
149
              xml.service(**args) do
6,023✔
150
                yield xml if block_given?
6,023✔
151
              end
152
            end
153
          end
154
        end
155

156
        # rubocop:disable Metrics/MethodLength
157
        def build_http_request(name:, form_file: nil)
4✔
158
          request = self.class.build_post_request
3,010✔
159

160
          log_xml_request(xml_payload)
3,010✔
161
          if form_file.nil?
3,010✔
162
            request["Content-Type"] = "text/xml; charset=utf-8"
3,009✔
163
            request.body = xml_payload(name:)
3,009✔
164
          else
165
            request["Content-Type"] = "multipart/form-data"
1✔
166
            request.set_form({ "request" => xml_payload,
1✔
167
                               "nb-data-attachments" => "1",
168
                               "file_0" => form_file },
169
                          "multipart/form-data",
170
                          "charset" => "UTF-8")
171
          end
172

173
          request
3,010✔
174
        end
175
      # rubocop:enable Metrics/MethodLength
176

177
      def log_xml_request(xml_payload)
4✔
178
        password_element = xml_payload.match(/\<password\>.*\<\/password\>/)
3,010✔
179
        if password_element.nil?
3,010✔
180
          Rails.logger.debug(xml_payload)
1,544✔
181
        else
182
          # Don't log the password
183
          Rails.logger.debug(xml_payload.gsub(password_element.to_s, "<password>***</password>"))
1,466✔
184
        end
185
      end
186

187
      # Logs as warning Mediaflux requests that take longer than 3 seconds.
188
      #
189
      # We could eventually also send to Honeybadger long requests but
190
      # let's wait until we have a benchmark of what is considered slow
191
      # in Mediaflux.
192
      def log_elapsed(start_time)
4✔
193
        elapsed_time = ::Time.zone.now - start_time
3,017✔
194
        timing_info = "#{format('%.2f', elapsed_time)} s"
3,017✔
195
        if elapsed_time > 3.0
3,017✔
196
          Rails.logger.warn "Slow Mediaflux request: #{self.class}, #{timing_info}"
2✔
197
        end
198
      end
199
    end
200
end
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