Module: Reports::Generator
- Defined in:
- app/commands/reports/generator.rb
Defined Under Namespace
Classes: GeneratorError
Class Method Summary collapse
-
.all_flattener_classes ⇒ Object
Lists all flatteners defined by reading the filesystem.
-
.build_option_name(*parts, column_type: nil, preserve_id: false) ⇒ Object
Examples # Removing redundant terms - in this case a second consecutive "Client" is removed.
- .check_resources ⇒ Object
- .create_resources(klass_name = nil) ⇒ Object
- .custom_select_options(klass, &block) ⇒ Object
- .generate_filter_options(klass, resource_id, **options) ⇒ Object
- .generate_select_options(klass, resource_id, **options) ⇒ Object
-
.run(klasses, verbose: false, **options) ⇒ Object
Arguments
klasses: A collection of the classes for which options will be generated. - .run_all ⇒ Object
Class Method Details
.all_flattener_classes ⇒ Object
Lists all flatteners defined by reading the filesystem. This is used to be able to obtain flattener names with prefixes like HasHelpers::Organization.
242 243 244 245 246 247 248 249 250 251 252 253 254 |
# File 'app/commands/reports/generator.rb', line 242 def self.all_flattener_classes base_path = Rails.root.join("app/models/flatteners") Dir.glob(base_path.join("**/*.rb")).filter_map do |path| const_name = Pathname.new(path). relative_path_from(base_path.parent). to_s. delete_suffix(".rb"). split("/"). map(&:camelize). join("::") const_name.safe_constantize end.select { |c| c.is_a?(Class) && c < Flattener::Base } end |
.build_option_name(*parts, column_type: nil, preserve_id: false) ⇒ Object
Examples
Removing redundant terms - in this case a second consecutive "Client" is removed.
build_option_name(:primary, :client, :client_id, preserve_id: false) # => "Primary Client Id"
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
# File 'app/commands/reports/generator.rb', line 272 def self.build_option_name(*parts, column_type: nil, preserve_id: false) # Create an enumerator which yields the words used to build the name. words = Enumerator.new do |yielder| parts.lazy. map(&:to_s). map do |part| if column_type == "boolean" part.gsub(/(^|_)is_/, '\1') else part end end. map do |part| if preserve_id part.tr("_", " ") # Prevents titleize from removing "_id" else part end end. map(&:titleize). each do |part| # Yield each word separately. E.g. "Client Id" => ["Client", "Id"] part.split(/\s+/).each do |sub_part| sub_part = sub_part.gsub("Id", "ID") yielder << sub_part end end end # Get all the parts of the name, excluding consecutive repeated elements. sanitized_parts = [*words.first] # Always include the first word words.each_cons(2) do |part1, part2| sanitized_parts << part2 unless part1 == part2 end sanitized_parts.join(" ") end |
.check_resources ⇒ Object
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
# File 'app/commands/reports/generator.rb', line 155 def self.check_resources # Fetch all resources with error handling all_resources = Reports::ExternalGraphqlClient.query_all_resources unless all_resources.key?("data") Rails.logger.error "Failed to fetch resources: #{all_resources.inspect}" return [] end resource_array = all_resources.dig("data", "resources") || [] # Get all flattener classes more efficiently flatteners = all_flattener_classes # Extract resource names once resource_names = (resource_array["edges"] || []).filter_map { |edge| edge["node"]&.dig("name")&.demodulize }.to_set # Find and process missing flatteners missing_flatteners = flatteners.each_with_object([]) do |flattener, arr| flattener_name = flattener.name.demodulize arr << flattener unless resource_names.include?(flattener_name) end # Create missing resources with logging missing_flatteners.each do |flattener| flattener_name = flattener.name.demodulize next if flattener_name.blank? create_resources(flattener_name) end missing_flatteners end |
.create_resources(klass_name = nil) ⇒ Object
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'app/commands/reports/generator.rb', line 110 def self.create_resources(klass_name = nil) # This method is used to ensure that the resources are created in the database. # It will create the resources if they do not exist. # It will return an array of errors if any resources could not be created. = [] if klass_name response = Reports::ExternalGraphqlClient.query_resource_by_name(name: klass_name) resource_id = response.dig("data", "resourceName", "id") unless resource_id # If not found, create it using the mutation create_resource = Reports::ExternalGraphqlClient.create_resource(mutation_name: "createResource", input_name: "ReportResourceInput", attributes: { name: klass_name, type: "HasReports::Resource" }) if create_resource["errors"] << "Failed to create resource for flattener #{klass_name}: #{create_resource["errors"]}" else << "Successfully created resource for flattener #{klass_name}" end end else flatteners = all_flattener_classes flatteners.each do |flattener| # Check if the resource exists via the external GraphQL client flattener_name = flattener.name.sub(/^Flatteners::/, "") # Remove the Flatteners:: prefix response = Reports::ExternalGraphqlClient.query_resource_by_name(name: flattener_name) resource_id = response.dig("data", "resourceName", "id") unless resource_id # If not found, create it using the mutation create_resource = Reports::ExternalGraphqlClient.create_resource( mutation_name: "createResource", input_name: "ReportResourceInput", attributes: { name: flattener_name, type: "HasReports::Resource" } ) if create_resource["errors"] << "Failed to create resource for flattener #{flattener_name}: #{create_resource["errors"]}" else << "Successfully created resource for flattener #{flattener_name}" end end end end end |
.custom_select_options(klass, &block) ⇒ Object
256 257 258 259 260 261 262 263 264 265 266 267 |
# File 'app/commands/reports/generator.rb', line 256 def self.(klass, &block) if block = [klass] [klass] = proc do |results = []| # Invoke the previous callback (if any) .call(results) if results.concat(block.call) end else [klass].nil? ? [] : [klass].call end end |
.generate_filter_options(klass, resource_id, **options) ⇒ Object
230 231 232 233 234 235 236 237 238 |
# File 'app/commands/reports/generator.rb', line 230 def self.(klass, resource_id, **) [].tap do |results| results.concat(::Reports::BaseOptions.(klass, **).to_a) results.concat(::Reports::ConstantOptions.(klass, **).to_a) results.concat(::Reports::BelongsToOptions.(klass, resource_id, **).to_a) results.concat(::Reports::HasManyOptions.(klass, **).to_a) results.concat(::Reports::NestedAttributeOptions.(klass, **).to_a) end end |
.generate_select_options(klass, resource_id, **options) ⇒ Object
220 221 222 223 224 225 226 227 228 |
# File 'app/commands/reports/generator.rb', line 220 def self.(klass, resource_id, **) results = [] results.concat(::Reports::BaseOptions.(klass, resource_id, **).to_a) results.concat(::Reports::ConstantOptions.(klass, **).to_a) results.concat(::Reports::BelongsToOptions.(klass, **).to_a) results.concat(::Reports::HasManyOptions.(klass, **).to_a) results.concat(::Reports::NestedAttributeOptions.(klass, **).to_a) results end |
.run(klasses, verbose: false, **options) ⇒ Object
Arguments
klasses: A collection of the classes for which options will be generated. Eg. Generator.run([Advisor, Policy])
Options
:schema: Optional schema name. This will be the default schema for all table names.
Examples
::Reports::Generator.run(Advisor) ::Reports::Generator.run_all --> to create resources and run for all flatteners
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'app/commands/reports/generator.rb', line 50 def self.run( klasses, # audit: true, verbose: false, ** ) aggregate_errors = [] # Generate the options inside a transaction. This ensures that if there # are any errors, we can rollback the entire generate, including any # modifications made in the `before_generate` hook. ::ActiveRecord::Base.transaction do # if defined? @@before_generate_block # class_eval(&@@before_generate_block) # end begin Array(klasses).each do |klass| # Check klass for has_reports # raise GeneratorError.new(full_error_list: GeneratorError::MISSING_HAS_REPORTS_MSG % { class_name: klass.name }) unless klass.has_reports(meta_only: true) # just one request per class # each time we run the generator we need to check if all flatteners are created on db as a resource check_resources response = Reports::ExternalGraphqlClient.query_resource_by_name(name: klass.name) resource_id = response.dig("data", "resourceName", "id") # Select options begin # first generate the resource if it doesnt exist # call the mutation to generate the resource select_option_attributes = (klass, resource_id, **) importer = ::Reports::Importer.new(resource_id, resource_name: klass.name, **) # select_option_attributes.uniq! { |attrs| attrs[:name] } # FIXME: temporary work-around until duplicate entries are fixed - Gotta test those reports!!! * errors = importer.(select_option_attributes, resource_id, verbose: verbose) if errors.any? aggregate_errors.concat(errors) end rescue ::Reports::Importer::AuditError => e aggregate_errors << GeneratorError.(msg: GeneratorError::ERROR_MSG % { error_type: "AUDIT", option_type: "SelectOption", option_name: klass.name }, base_error: e) end # Filter options begin importer = ::Reports::Importer.new(resource_id, resource_name: klass.name, **) filter_option_attributes = (klass, resource_id, **) # filter_option_attributes.uniq! { |attrs| attrs[:name] } # FIXME: temporary work-around until duplicate entries are fixed - Gotta test those reports!!! * errors = importer.(filter_option_attributes, resource_id, verbose: verbose) if errors.any? aggregate_errors.concat(errors) end rescue ::Reports::Importer::AuditError => e aggregate_errors << GeneratorError.(msg: GeneratorError::ERROR_MSG % { error_type: "AUDIT", option_type: "FilterOption", option_name: klass.name }, base_error: e) end end rescue => e aggregate_errors << GeneratorError.(msg: "FATAL EXCEPTION", base_error: e) end # Return errors aggregate_errors end end |
.run_all ⇒ Object
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'app/commands/reports/generator.rb', line 183 def self.run_all = [] flatteners = all_flattener_classes flatteners.each do |flattener| # Check if the resource exists via the external GraphQL client flattener_name = flattener.name.sub(/^Flatteners::/, "") # Remove the Flatteners:: prefix response = Reports::ExternalGraphqlClient.query_resource_by_name(name: flattener_name) resource_name = response.dig("data", "resourceName", "name") if resource_name.present? run(resource_name.constantize) unless resource_name.to_s.demodulize == "UserOrganization" else # If not found, create it using the mutation create_resource = Reports::ExternalGraphqlClient.create_resource( mutation_name: "createResource", input_name: "ReportResourceInput", attributes: { name: flattener_name, type: "HasReports::Resource" } ) if create_resource["errors"] << "Failed to create resource for flattener #{flattener_name}: #{create_resource["errors"]}" else resource_name = Reports::ExternalGraphqlClient.query_resource_by_name(name: flattener_name) resource_name = resource_name.dig("data", "resourceName", "name") klass = resource_name&.safe_constantize if klass run(klass) << "Successfully created resource for flattener #{flattener_name}" else << "Resource created but class not found for #{flattener_name}" end end end end end |