class PostImportRowNormaliser ORIGINS = ['automatic', 'mapped', 'fixed', 'manual'].freeze STRING_FIELDS = [ 'title', 'thumbnail_base', 'original_created_from', 'original_created_before', 'tags', 'parent_post_ids', ].freeze FLEXIBLE_FIELDS = ['duration', 'video_ms'].freeze ATTRIBUTE_FIELDS = (STRING_FIELDS + FLEXIBLE_FIELDS).freeze def self.normalise! rows, allow_warning_fields: false raise ArgumentError, '取込行の形式が不正です.' unless rows.is_a?(Array) raise ArgumentError, '取込件数が多すぎます.' if rows.length > PostImportSourceParser::MAX_ROWS normalised_rows = rows.map { normalise_row!(_1, allow_warning_fields:) } source_rows = normalised_rows.map { _1['source_row'] } raise ArgumentError, '元行番号が重複しています.' if source_rows.uniq.length != source_rows.length if normalised_rows.sum { row_bytesize(_1) } > PostImportSourceParser::MAX_BYTES raise ArgumentError, '取込データが大きすぎます.' end normalised_rows end def self.normalise_row! row, allow_warning_fields: unless row.is_a?(Hash) || row.is_a?(ActionController::Parameters) raise ArgumentError, '取込行の形式が不正です.' end parameters = row.is_a?(ActionController::Parameters) ? row : ActionController::Parameters.new(row) permitted = parameters.permit(*permitted_keys(allow_warning_fields)) normalised = permitted.to_h.deep_transform_keys { _1.to_s.underscore } normalised['source_row'] = normalise_source_row!(normalised['source_row']) normalise_url!(normalised['url']) normalise_metadata_url!(normalised['metadata_url']) normalise_attributes!(normalised.fetch('attributes', { })) normalise_provenance!(normalised.fetch('provenance', { })) normalise_tag_sources!(normalised['tag_sources']) normalise_warning_values!(normalised, allow_warning_fields:) if row_bytesize(normalised) > PostImportSourceParser::MAX_BYTES raise ArgumentError, '取込行が大きすぎます.' end normalised end def self.permitted_keys allow_warning_fields keys = [ :source_row, :sourceRow, :url, :metadata_url, :metadataUrl, { attributes: {} }, { provenance: {} }, { tag_sources: {} }, { tagSources: {} }, ] return keys unless allow_warning_fields keys + [ { fetch_warnings: [] }, { fetchWarnings: [] }, { validation_warnings: [] }, { validationWarnings: [] }, ] end private_class_method :permitted_keys def self.normalise_source_row! value source_row = Integer(value, exception: false) raise ArgumentError, '元行番号が不正です.' if source_row.nil? || source_row <= 0 source_row end private_class_method :normalise_source_row! def self.normalise_url! value raise ArgumentError, 'URL の形式が不正です.' unless value.is_a?(String) raise ArgumentError, 'URL が長すぎます.' if value.bytesize > PostImportSourceParser::MAX_CELL_BYTES end private_class_method :normalise_url! def self.normalise_metadata_url! value raise ArgumentError, 'metadata_url の形式が不正です.' unless value.nil? || value.is_a?(String) if value.to_s.bytesize > PostImportSourceParser::MAX_CELL_BYTES raise ArgumentError, 'metadata_url が長すぎます.' end end private_class_method :normalise_metadata_url! def self.normalise_attributes! attributes raise ArgumentError, 'attributes の形式が不正です.' unless attributes.is_a?(Hash) raise ArgumentError, '取込項目が不正です.' unless (attributes.keys - ATTRIBUTE_FIELDS).empty? attributes.each do |key, value| case key when *STRING_FIELDS raise ArgumentError, '取込項目の型が不正です.' unless value.nil? || value.is_a?(String) when *FLEXIBLE_FIELDS unless value.nil? || value.is_a?(String) || value.is_a?(Numeric) raise ArgumentError, '取込項目の型が不正です.' end end if value.to_s.bytesize > PostImportSourceParser::MAX_CELL_BYTES raise ArgumentError, '取込項目が大きすぎます.' end end end private_class_method :normalise_attributes! def self.normalise_provenance! provenance raise ArgumentError, 'provenance の形式が不正です.' unless provenance.is_a?(Hash) allowed = ATTRIBUTE_FIELDS + ['url'] raise ArgumentError, '値の由来が不正です.' unless (provenance.keys - allowed).empty? raise ArgumentError, '値の由来が不正です.' unless provenance.values.all? { ORIGINS.include?(_1) } end private_class_method :normalise_provenance! def self.normalise_tag_sources! tag_sources return if tag_sources.nil? raise ArgumentError, 'タグ由来の形式が不正です.' unless tag_sources.is_a?(Hash) raise ArgumentError, 'タグ由来の形式が不正です.' unless (tag_sources.keys - ORIGINS).empty? raise ArgumentError, 'タグ由来の形式が不正です.' unless tag_sources.values.all? { _1.is_a?(String) } if tag_sources.values.any? { _1.bytesize > PostImportSourceParser::MAX_CELL_BYTES } raise ArgumentError, 'タグ由来が大きすぎます.' end if tag_sources.values.sum(&:bytesize) > PostImportSourceParser::MAX_CELL_BYTES raise ArgumentError, 'タグ由来が大きすぎます.' end end private_class_method :normalise_tag_sources! def self.normalise_warning_values! normalised, allow_warning_fields: return unless allow_warning_fields %w[fetch_warnings validation_warnings].each do |field| values = normalised[field] next if values.nil? unless values.is_a?(Array) && values.all? { _1.is_a?(String) } raise ArgumentError, '警告の形式が不正です.' end if values.any? { _1.bytesize > PostImportSourceParser::MAX_CELL_BYTES } raise ArgumentError, '警告が大きすぎます.' end end end private_class_method :normalise_warning_values! def self.row_bytesize row row.to_json.bytesize end private_class_method :row_bytesize end