danger-app_size_report 0.0.1 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +3 -1
- data/Gemfile.lock +170 -0
- data/Guardfile +2 -0
- data/LICENSE +21 -0
- data/README.md +46 -8
- data/Rakefile +2 -0
- data/Resources/App Thinning Size Report.txt +57 -0
- data/Resources/expectedReportJSON.json +590 -0
- data/danger-app_size_report.gemspec +13 -11
- data/lib/app_size_report/gem_version.rb +3 -1
- data/lib/app_size_report/plugin.rb +152 -78
- data/lib/converter/helper/json_converter.rb +14 -0
- data/lib/converter/helper/memory_size.rb +107 -0
- data/lib/converter/helper/result_factory.rb +34 -0
- data/lib/converter/models/app_size_model.rb +32 -0
- data/lib/converter/models/device_model.rb +17 -0
- data/lib/converter/models/variant_model.rb +24 -0
- data/lib/converter/parser/app_size_parser.rb +48 -0
- data/lib/converter/parser/model_parser.rb +13 -0
- data/lib/converter/parser/report_parser.rb +65 -0
- data/lib/converter/parser/variant_descriptor_parser.rb +53 -0
- data/lib/converter/parser/variant_parser.rb +10 -0
- data/lib/danger_app_size_report.rb +3 -1
- data/lib/danger_plugin.rb +3 -1
- data/spec/app_size_report_spec.rb +18 -27
- data/spec/spec_helper.rb +19 -17
- metadata +35 -21
- data/LICENSE.txt +0 -22
@@ -1,111 +1,185 @@
|
|
1
|
-
|
2
|
-
require 'set'
|
1
|
+
# frozen_string_literal: false
|
3
2
|
|
4
3
|
module Danger
|
5
|
-
|
4
|
+
require 'json'
|
5
|
+
require_relative '../converter/parser/report_parser'
|
6
|
+
require_relative '../converter/helper/memory_size'
|
7
|
+
|
8
|
+
# A Danger plugin for reporting iOS app size violations.
|
9
|
+
# A valid App Thinning Size Report must be passed to the plugin
|
10
|
+
# for accurate functionality.
|
11
|
+
#
|
12
|
+
# @example Report app size violations if one or more App variants
|
13
|
+
# exceed 4GB.
|
14
|
+
#
|
15
|
+
# report_path = "/Path/to/AppSize/Report.txt"
|
16
|
+
# app_size_report.flag_violations(
|
17
|
+
# report_path,
|
18
|
+
# build_type: 'App',
|
19
|
+
# size_limit: 4,
|
20
|
+
# limit_unit: 'GB',
|
21
|
+
# fail_on_warning: false
|
22
|
+
# )
|
23
|
+
#
|
24
|
+
# @example Report app size violations if one or more App Clip variants
|
25
|
+
# exceed 8MB.
|
6
26
|
#
|
7
|
-
#
|
27
|
+
# report_path = "/Path/to/AppSize/Report.txt"
|
28
|
+
# app_size_report.flag_violations(
|
29
|
+
# report_path,
|
30
|
+
# build_type: 'Clip',
|
31
|
+
# size_limit: 8,
|
32
|
+
# limit_unit: 'MB',
|
33
|
+
# fail_on_warning: false
|
34
|
+
# )
|
8
35
|
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# # The result is sent to the pull request with a markdown format and
|
12
|
-
# # notifies failure which variants is violating the size limit.
|
36
|
+
# @example Fail PR if one or more App Clip variants exceed 8MB.
|
13
37
|
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
38
|
+
# report_path = "/Path/to/AppSize/Report.txt"
|
39
|
+
# app_size_report.flag_violations(
|
40
|
+
# report_path,
|
41
|
+
# build_type: 'Clip',
|
42
|
+
# size_limit: 8,
|
43
|
+
# limit_unit: 'MB',
|
44
|
+
# fail_on_warning: true
|
45
|
+
# )
|
46
|
+
#
|
47
|
+
# @example Get JSON string representation of app thinning size report
|
48
|
+
#
|
49
|
+
# report_path = "/Path/to/AppSize/Report.txt"
|
50
|
+
# app_size_json = app_size_report.report_json(report_path)
|
51
|
+
#
|
52
|
+
# @see ChargePoint/danger-app_size_report
|
53
|
+
# @tags ios, xcode, appclip, thinning, size
|
18
54
|
#
|
19
55
|
class DangerAppSizeReport < Plugin
|
20
|
-
#
|
21
|
-
# @
|
56
|
+
# Reports app size violations given a valid App Thinning Size Report.
|
57
|
+
# @param [String, required] report_path
|
58
|
+
# Path to valid App Thinning Size Report text file.
|
59
|
+
# @param [String, optional] build_type
|
60
|
+
# Specify whether the report corresponds to an App or an App Clip.
|
61
|
+
# Default: 'App'
|
62
|
+
# Supported values: 'App', 'Clip'
|
63
|
+
# @param [Numeric, optional] size_limit
|
64
|
+
# Specify the app size limit.
|
65
|
+
# Default: 4
|
66
|
+
# @param [String, optional] limit_unit
|
67
|
+
# Specific the unit for the given size limit.
|
68
|
+
# Default: 'GB'
|
69
|
+
# Supported values: 'KB', 'MB', 'GB'
|
70
|
+
# @param [Boolean, optional] fail_on_warning
|
71
|
+
# Specify whether the PR should fail if one or more app variants
|
72
|
+
# exceed the given size limit. By default, the plugin issues
|
73
|
+
# a warning in this case.
|
74
|
+
# Default: 'false'
|
75
|
+
# @return [void]
|
22
76
|
#
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
json = JSON.parse(report)
|
27
|
-
|
28
|
-
flagged_variants = parse_json(*json, app_size_limit)
|
29
|
-
|
30
|
-
if flagged_variants.length > 0
|
31
|
-
failure "The App Clip size limit of 10 MB has been exceeded by one or more variants"
|
32
|
-
end
|
77
|
+
def flag_violations(report_path, build_type: 'App', size_limit: 4, limit_unit: 'GB', fail_on_warning: false)
|
78
|
+
report_text = File.read(report_path)
|
79
|
+
variants = ReportParser.parse(report_text)
|
33
80
|
|
34
|
-
|
35
|
-
|
36
|
-
init_ads()
|
81
|
+
unless %w[App Clip].include? build_type
|
82
|
+
raise ArgumentError, "The 'build_type' argument only accepts the values \"App\" and \"Clip\""
|
37
83
|
end
|
38
|
-
end
|
39
84
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
json.each do |variant, value|
|
47
|
-
app_size = variant["app_size"]["uncompressed"]["value"]
|
48
|
-
app_odr_size = variant["app_on_demand_resources_size"]["uncompressed"]["value"]
|
49
|
-
if app_size > app_size_limit || app_odr_size > app_size_limit
|
50
|
-
flagged_variants.append(variant)
|
51
|
-
end
|
85
|
+
raise ArgumentError, "The 'size_limit' argument only accepts numeric values" unless size_limit.is_a? Numeric
|
86
|
+
|
87
|
+
limit_unit.upcase!
|
88
|
+
unless %w[KB MB GB].include? limit_unit
|
89
|
+
raise ArgumentError, "The 'build_type' argument only accepts the values \"KB\", \"MB\" and \"GB\""
|
52
90
|
end
|
53
|
-
|
54
|
-
|
55
|
-
|
91
|
+
|
92
|
+
unless [true, false].include? fail_on_warning
|
93
|
+
raise ArgumentError, "The 'fail_on_warning' argument only accepts the values 'true' and 'false'"
|
56
94
|
end
|
57
95
|
|
58
|
-
|
96
|
+
generate_size_report_markdown(variants, build_type, size_limit, limit_unit, fail_on_warning)
|
97
|
+
generate_variant_descriptors_markdown(variants)
|
98
|
+
generate_ads_label_markdown
|
59
99
|
end
|
60
100
|
|
61
|
-
#
|
62
|
-
#
|
101
|
+
# Returns a JSON string representation of the given App Thinning Size Report.
|
102
|
+
# @param [String, required] report_path
|
103
|
+
# Path to valid App Thinning Size Report text file.
|
104
|
+
# @return [String]
|
63
105
|
#
|
64
|
-
def
|
106
|
+
def report_json(report_path)
|
107
|
+
report_text = File.read(report_path)
|
108
|
+
variants = ReportParser.parse(report_text)
|
109
|
+
JSON.pretty_generate(variants)
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def generate_size_report_markdown(variants, build_type, size_limit, limit_unit, fail_on_warning)
|
115
|
+
limit_size = MemorySize.new("#{size_limit}#{limit_unit}")
|
116
|
+
|
117
|
+
if build_type == 'Clip' && limit_size.megabytes > 10
|
118
|
+
message "The size limit was set to 10 MB as the given limit of #{size_limit} #{limit_unit} exceeds Apple's App Clip size restrictions"
|
119
|
+
size_limit = 10
|
120
|
+
limit_unit = 'MB'
|
121
|
+
limit_size.kilobytes = 10 * 1024
|
122
|
+
elsif build_type == 'App' && limit_size.gigabytes > 4
|
123
|
+
message "The size limit was set to 4 GB as the given limit of #{size_limit} #{limit_unit} exceeds Apple's App size restrictions"
|
124
|
+
size_limit = 4
|
125
|
+
limit_unit = 'GB'
|
126
|
+
limit_size.kilobytes = 4 * 1024 * 1024
|
127
|
+
end
|
128
|
+
|
129
|
+
flagged_variant_names = []
|
130
|
+
variants.each do |variant|
|
131
|
+
if variant.app_size.uncompressed.value > limit_size.megabytes || variant.on_demand_resources_size.uncompressed.value > limit_size.megabytes
|
132
|
+
flagged_variant_names.append(variant.variant)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
if flagged_variant_names.length.positive?
|
137
|
+
if fail_on_warning
|
138
|
+
failure "The size limit of #{size_limit} #{limit_unit.upcase} has been exceeded by one or more variants"
|
139
|
+
else
|
140
|
+
warn "The size limit of #{size_limit} #{limit_unit.upcase} has been exceeded by one or more variants"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
65
144
|
size_report = "# App Thinning Size Report\n"
|
66
|
-
size_report << "### Size limit = #{
|
145
|
+
size_report << "### Size limit = #{size_limit} #{limit_unit.upcase}\n\n"
|
67
146
|
size_report << "| Under Limit | Variant | App Size - Compressed | App Size - Uncompressed | ODR Size - Compressed | ODR Size - Uncompressed |\n"
|
68
|
-
size_report << "|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
147
|
+
size_report << "| :-: | :-: | :-: | :-: | :-: | :-: |\n"
|
148
|
+
|
149
|
+
flagged_variants_set = flagged_variant_names.to_set
|
150
|
+
|
151
|
+
variants.each do |variant|
|
152
|
+
is_violating = flagged_variants_set.include?(variant.variant) ? '❌' : '✅'
|
153
|
+
app_size_compressed = "#{variant.app_size.compressed.value} #{variant.app_size.compressed.unit}"
|
154
|
+
app_size_uncompressed = "#{variant.app_size.uncompressed.value} #{variant.app_size.uncompressed.unit}"
|
155
|
+
odr_size_compressed = "#{variant.on_demand_resources_size.compressed.value} #{variant.on_demand_resources_size.compressed.unit}"
|
156
|
+
odr_size_uncompressed = "#{variant.on_demand_resources_size.uncompressed.value} #{variant.on_demand_resources_size.uncompressed.unit}"
|
157
|
+
|
158
|
+
size_report << "#{is_violating} | #{variant.variant} | #{app_size_compressed} | #{app_size_uncompressed} | #{odr_size_compressed} | #{odr_size_uncompressed} |\n"
|
78
159
|
end
|
79
160
|
|
80
161
|
markdown size_report
|
81
162
|
end
|
82
163
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
variant_descriptor_report << "| ----- | ---------------- | \n"
|
93
|
-
variant["supported_variant_descriptors"].each do |model, _|
|
94
|
-
variant_descriptor_report << "#{model["device"]} | #{model["os_version"]} | \n"
|
164
|
+
def generate_variant_descriptors_markdown(variants)
|
165
|
+
variant_descriptors_report = "### Supported Variant Descriptors \n\n"
|
166
|
+
variants.each do |variant|
|
167
|
+
variant_descriptors_report << "<details> \n"
|
168
|
+
variant_descriptors_report << "<summary> #{variant.variant} </summary> \n\n"
|
169
|
+
variant_descriptors_report << "| Model | Operating System | \n"
|
170
|
+
variant_descriptors_report << "| - | :-: |\n"
|
171
|
+
variant.supported_variant_descriptors.each do |variant_descriptor|
|
172
|
+
variant_descriptors_report << "#{variant_descriptor.device} | #{variant_descriptor.os_version} | \n"
|
95
173
|
end
|
96
|
-
|
174
|
+
variant_descriptors_report << "</details> \n\n"
|
97
175
|
end
|
98
176
|
|
99
|
-
markdown
|
177
|
+
markdown variant_descriptors_report
|
100
178
|
end
|
101
179
|
|
102
|
-
|
103
|
-
|
104
|
-
#
|
105
|
-
def init_ads
|
106
|
-
ads_label = "Powered by [danger-app_size_report](https://github.com/ChargePoint)"
|
180
|
+
def generate_ads_label_markdown
|
181
|
+
ads_label = 'Powered by [danger-app_size_report](https://github.com/ChargePoint)'
|
107
182
|
markdown ads_label
|
108
183
|
end
|
109
|
-
|
110
184
|
end
|
111
185
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
class JSONConverter
|
6
|
+
def to_json(_options = {})
|
7
|
+
hash = {}
|
8
|
+
instance_variables.each do |var|
|
9
|
+
key = var.to_s[1..]
|
10
|
+
hash[key] = instance_variable_get(var)
|
11
|
+
end
|
12
|
+
JSON.pretty_generate(hash)
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
require_relative '../helper/json_converter'
|
4
|
+
class MemorySize < JSONConverter
|
5
|
+
attr_accessor :kilobytes
|
6
|
+
|
7
|
+
ZERO_SIZE = 'zero kb'.freeze
|
8
|
+
|
9
|
+
UNIT = {
|
10
|
+
bytes: 'B',
|
11
|
+
kilobytes: 'KB',
|
12
|
+
megabytes: 'MB',
|
13
|
+
gigabytes: 'GB'
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
def bytes
|
17
|
+
@kilobytes * 1024
|
18
|
+
end
|
19
|
+
|
20
|
+
def megabytes
|
21
|
+
@kilobytes / 1024
|
22
|
+
end
|
23
|
+
|
24
|
+
def gigabytes
|
25
|
+
@kilobytes / 1024 / 1024
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(text)
|
29
|
+
super()
|
30
|
+
value = parse_from(text)
|
31
|
+
|
32
|
+
@kilobytes = value || 0
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def parse_from(text)
|
38
|
+
text_to_memory_unit = {
|
39
|
+
'b' => :bytes,
|
40
|
+
'byte' => :bytes,
|
41
|
+
'bytes' => :bytes,
|
42
|
+
'kb' => :kilobytes,
|
43
|
+
'kilobyte' => :kilobytes,
|
44
|
+
'kilobytes' => :kilobytes,
|
45
|
+
'mb' => :megabytes,
|
46
|
+
'megabyte' => :megabytes,
|
47
|
+
'megabytes' => :megabytes,
|
48
|
+
'gb' => :gigabytes,
|
49
|
+
'gigabyte' => :gigabytes,
|
50
|
+
'gigabytes' => :gigabytes
|
51
|
+
}
|
52
|
+
|
53
|
+
unit = text_to_memory_unit[parse_units(text)]
|
54
|
+
size = parse_size(text)
|
55
|
+
|
56
|
+
return nil unless size
|
57
|
+
|
58
|
+
unit ||= :megabytes
|
59
|
+
|
60
|
+
case unit
|
61
|
+
when :bytes
|
62
|
+
kilobytes_from_bytes(size)
|
63
|
+
when :kilobytes
|
64
|
+
size
|
65
|
+
when :megabytes
|
66
|
+
kilobytes_from_megabytes(size)
|
67
|
+
when :gigabytes
|
68
|
+
kilobytes_from_gigabytes(size)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def parse_units(text)
|
73
|
+
return 'kb' if text.downcase == ZERO_SIZE
|
74
|
+
|
75
|
+
result = ''
|
76
|
+
|
77
|
+
text.each_char do |char|
|
78
|
+
result << char if char.match?(/[[:alpha:]]/) && char != '.' && char != ','
|
79
|
+
end
|
80
|
+
|
81
|
+
result.downcase
|
82
|
+
end
|
83
|
+
|
84
|
+
def parse_size(text)
|
85
|
+
return 0.to_f if text.downcase == ZERO_SIZE
|
86
|
+
|
87
|
+
result = ''
|
88
|
+
|
89
|
+
text.each_char do |char|
|
90
|
+
result << char if char.match?(/[[:digit:]]/) || char == '.' || char == ','
|
91
|
+
end
|
92
|
+
|
93
|
+
result.to_f
|
94
|
+
end
|
95
|
+
|
96
|
+
def kilobytes_from_bytes(bytes)
|
97
|
+
bytes / 1024
|
98
|
+
end
|
99
|
+
|
100
|
+
def kilobytes_from_megabytes(megabytes)
|
101
|
+
megabytes * 1024
|
102
|
+
end
|
103
|
+
|
104
|
+
def kilobytes_from_gigabytes(gigabytes)
|
105
|
+
gigabytes * 1024 * 1024
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../parser/variant_parser'
|
4
|
+
require_relative '../parser/variant_descriptor_parser'
|
5
|
+
require_relative '../parser/app_size_parser'
|
6
|
+
|
7
|
+
class ResultFactory
|
8
|
+
def self.parse(from_text: '', parser: nil)
|
9
|
+
result = nil
|
10
|
+
case parser
|
11
|
+
when :variant
|
12
|
+
variant_parser = VariantParser.new(from_text)
|
13
|
+
variant_parser.parse
|
14
|
+
result = variant_parser.result
|
15
|
+
when :supported_variant_descriptors
|
16
|
+
variant_descriptor_parser = VariantDescriptorParser.new(from_text)
|
17
|
+
variant_descriptor_parser.parse
|
18
|
+
result = variant_descriptor_parser.result
|
19
|
+
when :app_on_demand_resources_size
|
20
|
+
app_size_parser = AppSizeParser.new(from_text)
|
21
|
+
app_size_parser.parse
|
22
|
+
result = app_size_parser.result
|
23
|
+
when :app_size
|
24
|
+
app_size_parser = AppSizeParser.new(from_text)
|
25
|
+
app_size_parser.parse
|
26
|
+
result = app_size_parser.result
|
27
|
+
when :on_demand_resources_size
|
28
|
+
app_size_parser = AppSizeParser.new(from_text)
|
29
|
+
app_size_parser.parse
|
30
|
+
result = app_size_parser.result
|
31
|
+
end
|
32
|
+
result
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../helper/json_converter'
|
4
|
+
class AppSizeModel < JSONConverter
|
5
|
+
attr_reader :compressed, :uncompressed
|
6
|
+
|
7
|
+
PARSING_KEYS = {
|
8
|
+
compressed: 'compressed',
|
9
|
+
uncompressed: 'uncompressed'
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
def initialize(compressed: SizeModel.placeholder, uncompressed: SizeModel.placeholder)
|
13
|
+
super()
|
14
|
+
@compressed = compressed
|
15
|
+
@uncompressed = uncompressed
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class SizeModel < JSONConverter
|
20
|
+
attr_reader :raw_value, :value, :unit
|
21
|
+
|
22
|
+
def initialize(raw_value, value, unit)
|
23
|
+
super()
|
24
|
+
@raw_value = raw_value
|
25
|
+
@value = value
|
26
|
+
@unit = unit
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.placeholder
|
30
|
+
SizeModel.new('Unknown', 0, MemorySize::UNIT[:bytes])
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../helper/json_converter'
|
4
|
+
class DeviceModel < JSONConverter
|
5
|
+
attr_reader :device, :os_version
|
6
|
+
|
7
|
+
PARSING_KEYS = {
|
8
|
+
device: 'device: ',
|
9
|
+
os_version: 'os-version: '
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
def initialize(device, os_version)
|
13
|
+
super()
|
14
|
+
@device = device
|
15
|
+
@os_version = os_version
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../helper/json_converter'
|
4
|
+
class VariantModel < JSONConverter
|
5
|
+
attr_reader :variant, :supported_variant_descriptors, :app_on_demand_resources_size, :app_size,
|
6
|
+
:on_demand_resources_size
|
7
|
+
|
8
|
+
PARSING_KEYS = {
|
9
|
+
variant: 'Variant: ',
|
10
|
+
supported_variant_descriptors: 'Supported variant descriptors: ',
|
11
|
+
app_on_demand_resources_size: 'App + On Demand Resources size: ',
|
12
|
+
app_size: 'App size: ',
|
13
|
+
on_demand_resources_size: 'On Demand Resources size: '
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
def initialize(variant, supported_variant_descriptors, app_on_demand_resources_size, app_size, on_demand_resources_size)
|
17
|
+
super()
|
18
|
+
@variant = variant
|
19
|
+
@supported_variant_descriptors = supported_variant_descriptors
|
20
|
+
@app_on_demand_resources_size = app_on_demand_resources_size
|
21
|
+
@app_size = app_size
|
22
|
+
@on_demand_resources_size = on_demand_resources_size
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
require_relative '../models/app_size_model'
|
4
|
+
require_relative '../helper/memory_size'
|
5
|
+
require_relative './model_parser'
|
6
|
+
|
7
|
+
class AppSizeParser < ModelParser
|
8
|
+
attr_reader :standardized_unit
|
9
|
+
|
10
|
+
def initialize(text, standardized_unit: MemorySize::UNIT[:megabytes])
|
11
|
+
super(text)
|
12
|
+
@standardized_unit = standardized_unit
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse
|
16
|
+
@text = @text.strip
|
17
|
+
if @text.empty?
|
18
|
+
@result = nil
|
19
|
+
else
|
20
|
+
parseable_text = @text.split(', ')
|
21
|
+
properties = {}
|
22
|
+
parsing_keys = AppSizeModel::PARSING_KEYS
|
23
|
+
parseable_text.each do |size_text|
|
24
|
+
parsing_keys.each do |_property, key|
|
25
|
+
if size_text.include?(key) && !properties.fetch(key, nil)
|
26
|
+
value = size_text.gsub!(key, '')
|
27
|
+
properties[key] = value.strip
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
compressed_string = properties.fetch(parsing_keys[:compressed], nil)
|
33
|
+
uncompressed_string = properties.fetch(parsing_keys[:uncompressed], nil)
|
34
|
+
compressed_value = MemorySize.new(compressed_string).megabytes
|
35
|
+
uncompressed_value = MemorySize.new(uncompressed_string).megabytes
|
36
|
+
|
37
|
+
if compressed_string && uncompressed_string && compressed_value && uncompressed_value
|
38
|
+
compressed_raw_value = compressed_string.downcase == MemorySize::ZERO_SIZE ? '0 KB' : compressed_string
|
39
|
+
compressed_size = SizeModel.new(compressed_raw_value, compressed_value, @standardized_unit)
|
40
|
+
uncompressed_raw_value = uncompressed_string.downcase == MemorySize::ZERO_SIZE ? '0 KB' : uncompressed_string
|
41
|
+
uncompressed_size = SizeModel.new(uncompressed_raw_value, uncompressed_value, @standardized_unit)
|
42
|
+
@result = AppSizeModel.new(compressed: compressed_size, uncompressed: uncompressed_size)
|
43
|
+
else
|
44
|
+
@result = AppSizeModel.new
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
require 'securerandom'
|
4
|
+
require_relative '../models/variant_model'
|
5
|
+
require_relative '../helper/result_factory'
|
6
|
+
|
7
|
+
class ReportParser
|
8
|
+
def self.parse(text)
|
9
|
+
splitter_id = SecureRandom.uuid
|
10
|
+
|
11
|
+
# First we trim the report text
|
12
|
+
preprocessed_data = text.strip
|
13
|
+
|
14
|
+
preprocessed_data.gsub!(/\n{2,3}/, "\n#{splitter_id}\n")
|
15
|
+
|
16
|
+
# Also append the splitter ID to the end of the text so we do not miss the last variant
|
17
|
+
preprocessed_data += "\n#{splitter_id}\n"
|
18
|
+
|
19
|
+
data = preprocessed_data.split("\n")
|
20
|
+
|
21
|
+
variants = []
|
22
|
+
dict = {}
|
23
|
+
|
24
|
+
data.each do |value|
|
25
|
+
parsing_keys = VariantModel::PARSING_KEYS
|
26
|
+
if value == splitter_id && dict.fetch(parsing_keys[:variant], nil)
|
27
|
+
variant = dict.fetch(parsing_keys[:variant], '')
|
28
|
+
supported_variant_descriptors = dict.fetch(parsing_keys[:supported_variant_descriptors], '')
|
29
|
+
app_on_demand_resources_size = dict.fetch(parsing_keys[:app_on_demand_resources_size], '')
|
30
|
+
app_size = dict.fetch(parsing_keys[:app_size], '')
|
31
|
+
on_demand_resources_size = dict.fetch(parsing_keys[:on_demand_resources_size], '')
|
32
|
+
|
33
|
+
# initialize variant model from all the parser result
|
34
|
+
model = VariantModel.new(variant,
|
35
|
+
supported_variant_descriptors,
|
36
|
+
app_on_demand_resources_size,
|
37
|
+
app_size,
|
38
|
+
on_demand_resources_size)
|
39
|
+
|
40
|
+
variants.append(model)
|
41
|
+
|
42
|
+
# reset all the properties
|
43
|
+
dict = {}
|
44
|
+
end
|
45
|
+
|
46
|
+
parsing_keys.each do |property, key|
|
47
|
+
next unless value.include? key
|
48
|
+
|
49
|
+
# clean the key from the text
|
50
|
+
# i.e. "Variant: ChargePointAppClip-354363463-...." remove the "Variant: " so we have a clean text that we can parse ("ChargePointAppClip-354363463-....")
|
51
|
+
# i.e. "Supported variant descriptors: [device: iPhone10,3, os-version:14.0], ..." will pass only the "[device: iPhone10,3, os-version:14.0], ..." to the parser
|
52
|
+
if (key == parsing_keys[:on_demand_resources_size]) && (value.include? parsing_keys[:app_on_demand_resources_size])
|
53
|
+
parseable_text = value.gsub(parsing_keys[:app_on_demand_resources_size], '')
|
54
|
+
dict[key] = parseable_text
|
55
|
+
dict[key] = ResultFactory.parse(from_text: parseable_text, parser: :app_on_demand_resources_size)
|
56
|
+
else
|
57
|
+
parseable_text = value.gsub(key, '')
|
58
|
+
dict[key] = parseable_text
|
59
|
+
dict[key] = ResultFactory.parse(from_text: parseable_text, parser: property)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
variants
|
64
|
+
end
|
65
|
+
end
|