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.
@@ -1,111 +1,185 @@
1
- require 'json'
2
- require 'set'
1
+ # frozen_string_literal: false
3
2
 
4
3
  module Danger
5
- # Validates variant sizes of app thinning report json within a Pull Request and generates a brief report.
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
- # @example Validating code coverage for app_th (easy-peasy.io)
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
- # # Get the path to your app thinning report
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
- # app_size_report.report(
15
- # [path_to_report],
16
- # [app_size_limit]
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
- # A method that create app thinning report
21
- # @return [Array<Variant>]
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 report(report_path, app_size_limit)
24
- if File.exist?(report_path)
25
- report = File.read(report_path)
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
- init_app_size_violation_table(*json, flagged_variants, app_size_limit)
35
- init_supported_variant_descriptors(*json)
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
- # A method that create an array of variants that violate app size limit
41
- # @return [Array<Variant>]
42
- #
43
- def parse_json(*json, app_size_limit)
44
- flagged_variants = Array.new
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
- if flagged_variants.length > 0
55
- failure "The App Clip size limit of 10 MB has been exceeded by one or more variants"
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
- return flagged_variants
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
- # A method that create the violation table in Github
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 init_app_size_violation_table(*json, flagged_variants, app_size_limit)
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 = #{app_size_limit} MB\n\n"
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 << "| ---------- | ------- | --------------------- | --------------------- | --------------------- | --------------------- |\n"
69
- flagged_variants_set = flagged_variants.to_set
70
- json.each do |variant, _|
71
- isViolating = flagged_variants_set.include?(variant) ? "❌" : "✅"
72
- app_size_compressed = "#{variant["app_size"]["compressed"]["value"]} #{variant["app_size"]["uncompressed"]["unit"]}"
73
- app_size_uncompressed = "#{variant["app_size"]["uncompressed"]["value"]} #{variant["app_size"]["uncompressed"]["unit"]}"
74
- odr_size_compressed = "#{variant["on_demand_resources_size"]["compressed"]["value"]} #{variant["app_size"]["uncompressed"]["unit"]}"
75
- odr_size_uncompressed = "#{variant["on_demand_resources_size"]["uncompressed"]["value"]} #{variant["app_size"]["uncompressed"]["unit"]}"
76
-
77
- size_report << "#{isViolating} | #{variant["variant"]} | #{app_size_compressed} | #{app_size_uncompressed} | #{odr_size_compressed} | #{odr_size_uncompressed} |\n"
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
- # A method that create the supported variant table in Github
84
- #
85
- #
86
- def init_supported_variant_descriptors(*json)
87
- variant_descriptor_report = "### Supported Variant Descriptors \n\n"
88
- json.each do |variant, _|
89
- variant_descriptor_report << "<details> \n"
90
- variant_descriptor_report << "<summary> Variant #{variant["variant"]} </summary> \n\n"
91
- variant_descriptor_report << "| Model | Operating System | \n"
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
- variant_descriptor_report << "</details> \n\n"
174
+ variant_descriptors_report << "</details> \n\n"
97
175
  end
98
176
 
99
- markdown variant_descriptor_report
177
+ markdown variant_descriptors_report
100
178
  end
101
179
 
102
- # A method that create the footnote label in Github
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,13 @@
1
+ # frozen_string_literal: false
2
+
3
+ class ModelParser
4
+ attr_reader :text, :result
5
+
6
+ def initialize(text)
7
+ @text = text
8
+ end
9
+
10
+ def parse
11
+ raise NotImplementedError, 'Implement this method in a child class'
12
+ end
13
+ 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