ecoportal-api-v2 1.1.8 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,7 +3,7 @@ module Ecoportal
3
3
  class V2
4
4
  class Page < Common::Content::DoubleModel
5
5
  ALLOWED_KEYS = %w[
6
- id patch_ver name template_id
6
+ id external_id patch_ver name template_id
7
7
  base_tags tags
8
8
  time_zone created_at updated_at
9
9
  components sections stages
@@ -14,6 +14,7 @@ module Ecoportal
14
14
  ].freeze
15
15
 
16
16
  passkey :id
17
+ passthrough :external_id
17
18
  passforced :patch_ver, default: 1
18
19
  passthrough :name, :template_id
19
20
  passarray :base_tags, :tags, order_matters: false
@@ -41,12 +42,13 @@ module Ecoportal
41
42
 
42
43
  def as_update
43
44
  super.tap do |hash|
44
- if hash
45
- hash["data"].select! do |key, value|
46
- ALLOWED_KEYS.include?(key)
47
- end
48
- return nil if (hash["data"].keys - ["patch_ver"]).empty?
45
+ next unless hash
46
+
47
+ hash["data"].select! do |key, _value|
48
+ ALLOWED_KEYS.include?(key)
49
49
  end
50
+
51
+ return nil if (hash["data"].keys - ["patch_ver"]).empty?
50
52
  end
51
53
  end
52
54
 
@@ -57,32 +59,33 @@ module Ecoportal
57
59
  # @return [String] with feedback, if for this page instance, there are any of:
58
60
  # 1. components multi-section (fields belonging to more than one section)
59
61
  def validate
60
- msg = ""
61
- if (multi = components.multi_section).length.positive?
62
- msg += "There are fields attached to more than one section:\n • "
63
- msg += multi.map do |fld|
64
- fld.label
65
- end.join("\n • ") + "\n"
66
- end
62
+ multi = components.multi_section
63
+ return true unless multi.length.positive?
67
64
 
68
- msg.empty?? true : msg
65
+ msg = ""
66
+ msg << "There are fields attached to more than one section:"
67
+ msg << "\n • "
68
+ msg << multi.map(&:label).join("\n • ")
69
+ msg << "\n"
70
+ msg
69
71
  end
70
72
 
71
73
  private
72
74
 
73
75
  def _doc_bug_fix(hash)
74
- hash.tap do |hash|
76
+ hash.tap do
75
77
  _fix_doc(hash["stages"], "flow_node_ids", "section_ids") if hash.key?("stages")
76
- if hash.key?("sections")
77
- _fix_doc(hash["sections"], "membrane_ids", "component_ids")
78
- _fix_doc(hash["sections"], "left_membrane_ids", "left_component_ids")
79
- _fix_doc(hash["sections"], "right_membrane_ids", "right_component_ids")
80
- end
78
+
79
+ next unless hash.key?("sections")
80
+
81
+ _fix_doc(hash["sections"], "membrane_ids", "component_ids")
82
+ _fix_doc(hash["sections"], "left_membrane_ids", "left_component_ids")
83
+ _fix_doc(hash["sections"], "right_membrane_ids", "right_component_ids")
81
84
  end
82
85
  end
83
86
 
84
87
  def _fix_doc(value, source, dest)
85
- value.tap do |value|
88
+ value.tap do
86
89
  case value
87
90
  when Array
88
91
  value.each {|v| _fix_doc(v, source, dest)}
@@ -91,7 +94,7 @@ module Ecoportal
91
94
  value[dest] = value[source]
92
95
  value.delete(source)
93
96
  end
94
- else
97
+ else # rubocop:disable Style/EmptyElse
95
98
  # Do nothing!
96
99
  end
97
100
  end
@@ -18,11 +18,10 @@ module Ecoportal
18
18
 
19
19
  # @return [String] unique id
20
20
  def uid
21
- if mould_counter? && counter = mould_counter
22
- counter.render
23
- else
24
- id
25
- end
21
+ return id unless mould_counter?
22
+ return id unless (counter = mould_counter)
23
+
24
+ counter.render
26
25
  end
27
26
 
28
27
  # @return [String] `id` of the stage we got the data of.
@@ -32,43 +31,46 @@ module Ecoportal
32
31
 
33
32
  # @return [Ecoportal::API::V2::Page::Stage]
34
33
  def current_stage
35
- if stage_id = current_stage_id
36
- stages[stage_id]
37
- end
34
+ return false unless (stage_id = current_stage_id)
35
+
36
+ stages[stage_id]
38
37
  end
39
38
 
40
39
  # @return [String] with feedback, if for this page instance, there are any of:
41
40
  # 1. orphaned components (fields not belonging to any section)
42
41
  # 2. orphaned sections (sections not belonging to any stage)
43
- def validate
42
+ def validate # rubocop:disable Metrics/AbcSize
44
43
  msg = super
45
44
  msg = "" unless msg.is_a?(String)
46
45
 
47
46
  orphans = components.unattached.select {|comp| comp.global_binding.to_s.strip.empty?}
48
- if orphans.length > 0
49
- msg += "There are fields not attached to any sections:\n • "
50
- msg += orphans.map do |fld|
51
- fld.label
52
- end.join("\n • ") + "\n"
47
+ if orphans.length.positive?
48
+ msg << "There are fields not attached to any sections:"
49
+ msg << "\n • "
50
+ msg << orphans.map(&:label).join("\n • ")
51
+ msg << "\n"
53
52
  end
54
53
 
55
- if (orphans = sections.unattached).length > 0
56
- msg += "There are sections not attached to any stage:\n • "
57
- msg += orphans.map do |sec|
54
+ if (orphans = sections.unattached).length.positive?
55
+ msg << "There are sections not attached to any stage:"
56
+ msg << "\n • "
57
+ msg << orphans.map do |sec|
58
58
  "'#{sec.heading}' (#{sec.id})"
59
- end.join("\n • ") + "\n"
59
+ end.join("\n • ")
60
+ msg << "\n"
60
61
  end
62
+
61
63
  msg.empty?? true : msg
62
64
  end
63
65
 
64
66
  def mark_as_submit
65
67
  doc["submitted"] = true
66
- doc["type"] = "complete_page"
68
+ doc["type"] = "complete_page"
67
69
  end
68
70
 
69
71
  def mark_as_sign_off
70
72
  doc["sign_off"] = true
71
- doc["type"] = "review_page"
73
+ doc["type"] = "review_page"
72
74
  end
73
75
  end
74
76
  end
@@ -1,16 +1,17 @@
1
1
  module Ecoportal
2
2
  module API
3
3
  class V2
4
- # @attr_reader client [Common::Client] a `Common::Client` object that holds the configuration of the api connection.
4
+ # @attr_reader client [Common::Client] a `Common::Client` object that
5
+ # holds the configuration of the api connection.
5
6
  class Pages
6
- STAGE_REX = /stages\/(?<sid>.*)/
7
+ STAGE_REX = /stages\/(?<sid>.*)/.freeze
7
8
  extend Common::BaseClass
8
9
  include Common::Content::DocHelpers
9
10
 
10
11
  class_resolver :stages_class, "Ecoportal::API::V2::Pages::Stages"
11
- class_resolver :page_class, "Ecoportal::API::V2::Page"
12
- class_resolver :page_stage_class, "Ecoportal::API::V2::Pages::PageStage"
13
- class_resolver :create_page_response_class, "Ecoportal::API::V2::Pages::PageCreateResponse"
12
+ class_resolver :page_class, "Ecoportal::API::V2::Page"
13
+ class_resolver :page_stage_class, "Ecoportal::API::V2::Pages::PageStage"
14
+ class_resolver :create_page_response_class, "Ecoportal::API::V2::Pages::PageCreateResponse"
14
15
 
15
16
  attr_reader :client
16
17
 
@@ -35,16 +36,19 @@ module Ecoportal
35
36
  # @return [Ecoportal::API::V2::Page, Ecoportal::API::V2::Pages::PageStage] the target page.
36
37
  def get(id, stage_id: nil)
37
38
  return stages.get(id: id, stage_id: stage_id) if stage_id
39
+
38
40
  id = get_id(id)
39
41
  response = client.get("/pages/#{CGI.escape(id)}")
40
42
  wrapped = Common::Content::WrappedResponse.new(response, page_class)
41
43
 
42
44
  return wrapped.result if wrapped.success?
43
- if (response.status == 302) && (url = response.body["data"])
44
- if stage_id = url_to_stage_id(url)
45
- return stages.get(id: id, stage_id: stage_id)
46
- end
47
- end
45
+
46
+ url = nil
47
+ url = response.body["data"] if response.status == 302
48
+ stage_id = url_to_stage_id(url) unless url.nil?
49
+
50
+ return stages.get(id: id, stage_id: stage_id) if stage_id
51
+
48
52
  raise "Could not get page #{id} - Error #{response.status}: #{response.body}"
49
53
  end
50
54
 
@@ -56,7 +60,8 @@ module Ecoportal
56
60
  body = get_body(doc) # , level: "page"
57
61
  # Launch only if there are changes
58
62
  raise "Missing page object" unless body && body["page"]
59
- id = get_id(doc)
63
+
64
+ id = get_id(doc)
60
65
  client.patch("/pages/#{CGI.escape(id)}", data: body)
61
66
  end
62
67
 
@@ -81,9 +86,9 @@ module Ecoportal
81
86
  body = get_body(doc).tap do |hash|
82
87
  unless hash["page"]
83
88
  hash["page"] = {
84
- "id" => "111111111111111111111111",
89
+ "id" => "111111111111111111111111",
85
90
  "operation" => "changed",
86
- "data" => {"patch_ver" => 0}
91
+ "data" => {"patch_ver" => 0}
87
92
  }
88
93
  end
89
94
  end
@@ -98,7 +103,6 @@ module Ecoportal
98
103
  def url_to_stage_id(url)
99
104
  (matches = url.match(STAGE_REX)) && matches[:sid]
100
105
  end
101
-
102
106
  end
103
107
  end
104
108
  end
@@ -1,9 +1,9 @@
1
1
  module Ecoportal
2
2
  module API
3
3
  class V2
4
- # @attr_reader client [Common::Client] a `Common::Client` object that holds the configuration of the api connection.
4
+ # @attr_reader client [Common::Client] a `Common::Client` object that
5
+ # holds the configuration of the api connection.
5
6
  class People < API::Internal::People
6
-
7
7
  def batch
8
8
  unavailable_method!(__method__)
9
9
  end
@@ -24,7 +24,6 @@ module Ecoportal
24
24
  def unavailable_method!(str)
25
25
  raise "Unavailable method '#{str}' for api '#{VERSION}'"
26
26
  end
27
-
28
27
  end
29
28
  end
30
29
  end
@@ -2,26 +2,29 @@ require 'base64'
2
2
  module Ecoportal
3
3
  module API
4
4
  class V2
5
- # @attr_reader client [Common::Client] a `Common::Client` object that holds the configuration of the api connection.
5
+ # @attr_reader client [Common::Client] a `Common::Client` object that
6
+ # holds the configuration of the api connection.
6
7
  class Registers
7
8
  extend Common::BaseClass
8
9
  include Enumerable
9
10
  include Common::Content::DocHelpers
10
11
 
11
- class_resolver :register_class, "Ecoportal::API::V2::Registers::Register"
12
+ class_resolver :register_class, "Ecoportal::API::V2::Registers::Register"
12
13
  class_resolver :register_search_result_class, "Ecoportal::API::V2::Registers::PageResult"
13
14
  class_resolver :register_search_results, "Ecoportal::API::V2::Registers::SearchResults"
14
15
 
15
16
  attr_reader :client
16
17
 
17
- # @param client [Common::Client] a `Common::Client` object that holds the configuration of the api connection.
18
+ # @param client [Common::Client] a `Common::Client` object that
19
+ # holds the configuration of the api connection.
18
20
  # @return [Registers] an instance object ready to make registers api requests.
19
21
  def initialize(client)
20
22
  @client = client
21
23
  end
22
24
 
23
25
  def each(params: {}, &block)
24
- return to_enum(:each) unless block
26
+ return to_enum(:each, params: params) unless block
27
+
25
28
  get.each(&block)
26
29
  end
27
30
 
@@ -29,7 +32,11 @@ module Ecoportal
29
32
  # @return [Enumerable<Register>] an `Enumerable` with all schemas already wrapped as `Register` objects.
30
33
  def get
31
34
  response = client.get("/templates")
32
- Common::Content::WrappedResponse.new(response, register_class, key: "registers")
35
+ Common::Content::WrappedResponse.new(
36
+ response,
37
+ register_class,
38
+ key: "registers"
39
+ )
33
40
  end
34
41
 
35
42
  # Gets all the oozes/pages of `register_id` matching the `options`
@@ -41,18 +48,21 @@ module Ecoportal
41
48
  # @yield [result] something to do with search page-result.
42
49
  # @yieldparam result [Ecoportal::V2::Registers::PageResult] a page result.
43
50
  # @return [Ecoportal::API::V2::Registers, Ecoportal::API::V2::Registers::SearchResults]
44
- def search(register_id, options = {})
51
+ def search(register_id, options = {}) # rubocop:disable Metrics/AbcSize
45
52
  only_first = options.delete(:only_first)
46
53
  options = build_options(options)
47
54
 
48
55
  if only_first
49
56
  response = client.get("/registers/#{register_id}/search", params: options)
50
57
  raise "Request failed - Status #{response.status}: #{response.body}" unless response.success?
58
+
51
59
  return register_search_results.new(response.body["data"])
52
60
  end
53
61
 
54
62
  cursor_id = nil
55
- results = 0; total = nil
63
+ results = 0
64
+ total = nil
65
+
56
66
  loop do
57
67
  options.update(cursor_id: cursor_id) if cursor_id
58
68
  response = client.get("/registers/#{register_id}/search", params: options)
@@ -61,12 +71,14 @@ module Ecoportal
61
71
  data = response.body["data"]
62
72
  total ||= data["total"]
63
73
  if total != data["total"]
64
- msg = "Change of total in search results. Probably due to changes that affect the filter (register: #{register_id}):"
74
+ msg = "Change of total in search results. "
75
+ msg << "Probably due to changes that affect the filter"
76
+ msg << "(register: #{register_id}):"
65
77
  print_search_status(msg, total, results, cursor_id, data, options)
66
78
  #total = data["total"]
67
79
  end
68
80
 
69
- unless total == 0
81
+ unless total&.zero?
70
82
  results += data["results"].length
71
83
  print_progress(results, total)
72
84
  end
@@ -77,12 +89,15 @@ module Ecoportal
77
89
  end
78
90
 
79
91
  break if total <= results
92
+
80
93
  unless data["cursor_id"]
81
- msg = "Possible error... finishing search for lack of cursor_id in response:"
94
+ msg = "Possible error... finishing search for lack of cursor_id in response:"
82
95
  print_search_status(msg, total, results, cursor_id, data, options)
83
96
  end
97
+
84
98
  break unless (cursor_id = data["cursor_id"])
85
99
  end
100
+
86
101
  self
87
102
  end
88
103
 
@@ -96,14 +111,14 @@ module Ecoportal
96
111
  options.each do |key, value|
97
112
  if key == :filters && value.any?
98
113
  ret[key] = {filters: value}.to_json
99
- else
100
- ret[key] = value if key
114
+ elsif key
115
+ ret[key] = value
101
116
  end
102
117
  end
103
118
  end
104
119
  end
105
120
 
106
- def print_search_status(msg, total, results, cursor_id, data, options)
121
+ def print_search_status(msg, total, results, cursor_id, data, options) # rubocop:disable Metrics/ParameterLists
107
122
  msg += "\n"
108
123
  msg += " • Original total: #{total}\n"
109
124
  msg += " • Current total: #{data&.dig("total")}\n"
@@ -0,0 +1,27 @@
1
+ module Ecoportal
2
+ module API
3
+ class V2
4
+ class S3
5
+ class Data < Common::Content::DoubleModel
6
+ passthrough :s3_endpoint, :AWSAccessKeyId
7
+ passthrough :policy, :signature
8
+ passthrough :user_tmpdir
9
+
10
+ def x_amz_server_side_encryption
11
+ doc['x-amz-server-side-encryption']
12
+ end
13
+
14
+ def [](key)
15
+ doc[key.to_s]
16
+ end
17
+
18
+ def user_id
19
+ return unless user_tmpdir
20
+
21
+ user_tmpdir.split('uploads/').last
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,110 @@
1
+ module Ecoportal
2
+ module API
3
+ class V2
4
+ class S3
5
+ class Files
6
+ # Class service to upload multiple files in one go
7
+ class BatchUpload
8
+ include Ecoportal::API::Common::Concerns::Benchmarkable
9
+ include Ecoportal::API::Common::Concerns::Threadable
10
+
11
+ SIZE_UNITS = 1_024 # KB
12
+ MIN_UNIT = 1
13
+
14
+ attr_reader :files, :files_api
15
+
16
+ FileResult = Struct.new(:file) do
17
+ attr_accessor :poll, :s3_file_reference, :error
18
+
19
+ def error?
20
+ !error.nil?
21
+ end
22
+
23
+ def success?
24
+ poll&.success?
25
+ end
26
+
27
+ def container_id
28
+ return unless success?
29
+ poll.container_id
30
+ end
31
+ end
32
+
33
+ # @param files [Array<String>] the files to be uploaded
34
+ # @param files_api [API::S3::Files] the api object.
35
+ def initialize(files, files_api:)
36
+ @files = [files].flatten.compact
37
+ @files_api = files_api
38
+ end
39
+
40
+ # Do the actual upload of the file
41
+ def upload!(benchmarking: false, threads: 1, **kargs) # rubocop:disable Metrics/AbcSize
42
+ bench = benchmarking && benchmark_enabled?
43
+ bench_mth = "#{self.class}##{__method__}"
44
+ thr_children = []
45
+ benchmarking("#{bench_mth}.#{files.count}_files", print: bench) do
46
+ with_preserved_thread_globals(report: false) do
47
+ files.each do |file|
48
+ new_thread(thr_children, max: threads) do
49
+ file_results << (result = FileResult.new(file))
50
+
51
+ s3_ref = nil
52
+
53
+ kbytes = bench ? file_size(file) : 1
54
+
55
+ benchmarking("#{bench_mth}.s3_upload (KB)", units: kbytes, print: bench) do
56
+ s3_ref = result.s3_file_reference = s3_api.upload_file(file)
57
+ end
58
+
59
+ benchmarking("##{bench_mth}.ep_poll (KB)", units: kbytes, print: bench) do
60
+ files_api.poll!(s3_ref, **kargs) do |poll|
61
+ result.poll = poll
62
+ end
63
+ end
64
+ yield(result) if block_given?
65
+ rescue *rescued_errors => err # each_file
66
+ result.error = err
67
+ yield(result) if block_given?
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ thr_children.each(&:join)
74
+ file_results
75
+ ensure
76
+ puts benchmark_summary(:all) if bench
77
+ end
78
+
79
+ private
80
+
81
+ def file_size(file, bytes: SIZE_UNITS)
82
+ return 1 unless File.exist?(file)
83
+
84
+ units = (File.size(file) / bytes).round(10)
85
+ [MIN_UNIT, units].max
86
+ end
87
+
88
+ def s3_api
89
+ files_api.s3_api
90
+ end
91
+
92
+ def file_results
93
+ @file_results ||= []
94
+ end
95
+
96
+ def rescued_errors
97
+ @rescued_errors ||= [
98
+ Ecoportal::API::V2::S3::MissingLocalFile,
99
+ Ecoportal::API::V2::S3::CredentialsGetFailed,
100
+ Ecoportal::API::V2::S3::Files::FailedPollRequest,
101
+ Ecoportal::API::V2::S3::Files::CantCheckStatus,
102
+ Ecoportal::API::Errors::TimeOut
103
+ ].freeze
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,82 @@
1
+ module Ecoportal
2
+ module API
3
+ class V2
4
+ class S3
5
+ class Files
6
+ class Poll < Common::Content::DoubleModel
7
+ passthrough :poll_url
8
+ embeds_one :status, nullable: true, klass: "Ecoportal::API::V2::S3::Files::PollStatus"
9
+
10
+ def poll_id
11
+ return @poll_id if instance_variable_defined?(:@poll_id)
12
+ return @poll_id = nil unless (uri = parsed_poll_url)
13
+
14
+ @poll_id = uri.path.split('/poll/').last
15
+ end
16
+ alias_method :id, :poll_id
17
+
18
+ # The final File eP container id
19
+ def container_id
20
+ return unless status?
21
+
22
+ status.container_id
23
+ end
24
+
25
+ def status?
26
+ !doc['status'].nil?
27
+ end
28
+
29
+ def status=(value)
30
+ case value
31
+ when NilClass
32
+ doc["status"] = nil
33
+ when Ecoportal::API::V2::S3::Files::PollStatus
34
+ doc["status"] = JSON.parse(value.to_json)
35
+ when Hash
36
+ doc["status"] = value
37
+ else
38
+ # TODO
39
+ raise "Invalid set on status: Need nil, PollStatus or Hash; got #{value.class}"
40
+ end
41
+
42
+ remove_instance_variable("@status") if defined?(@status)
43
+ end
44
+
45
+ def pending?
46
+ !complete?
47
+ end
48
+
49
+ def complete?
50
+ status&.complete?
51
+ end
52
+
53
+ def success?
54
+ status&.success?
55
+ end
56
+
57
+ def failed?
58
+ status&.failed?
59
+ end
60
+
61
+ def timeout?
62
+ status&.timeout?
63
+ end
64
+
65
+ private
66
+
67
+ def parsed_poll_url
68
+ return nil unless poll_url
69
+
70
+ require 'uri'
71
+ URI.parse(poll_url)
72
+ end
73
+
74
+ def callbacks
75
+ @callbacks ||= []
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,52 @@
1
+ module Ecoportal
2
+ module API
3
+ class V2
4
+ class S3
5
+ class Files
6
+ class PollStatus < Common::Content::DoubleModel
7
+ class FileContainer < Common::Content::DoubleModel
8
+ passkey :id
9
+ passthrough :file_container_id # same as :id :)
10
+ passthrough :name, :label
11
+ passthrough :file_size, :content_type
12
+ passthrough :tags
13
+ passboolean :archived
14
+ passthrough :active_person, :user_name
15
+ passdate :created_at, :updated_at, :file_update_at
16
+ end
17
+
18
+ # PollStatus
19
+ passthrough :status
20
+ embeds_one :file, klass: FileContainer
21
+
22
+ def container_id
23
+ return unless success?
24
+
25
+ file&.id
26
+ end
27
+
28
+ def complete?
29
+ success? || failed?
30
+ end
31
+
32
+ def timeout?
33
+ statut == 'timeout'
34
+ end
35
+
36
+ def pending?
37
+ status == "pending"
38
+ end
39
+
40
+ def success?
41
+ status == "success"
42
+ end
43
+
44
+ def failed?
45
+ status == "failed"
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end