162 行
6.0 KiB
Ruby
162 行
6.0 KiB
Ruby
class PostImportsController < ApplicationController
|
|
MAPPING_PROPERTIES = ['kind', 'value', 'columns'].freeze
|
|
MAPPING_KINDS = ['columns', 'fixed'].freeze
|
|
|
|
before_action :require_member!
|
|
|
|
def parse
|
|
source =
|
|
if params[:format] == 'google_sheets'
|
|
PostImportGoogleSheetsFetcher.fetch!(params[:source],
|
|
rate_key: current_user.id)
|
|
else
|
|
params[:source]
|
|
end
|
|
format = params[:format] == 'google_sheets' ? 'csv' : params[:format]
|
|
parsed = PostImportSourceParser.new(
|
|
source:,
|
|
format:,
|
|
has_header: params[:has_header],
|
|
json_path: params[:json_path]).parse
|
|
render json: parsed
|
|
rescue ArgumentError => e
|
|
render_bad_request(e.message)
|
|
end
|
|
|
|
def preview
|
|
parsed = parsed_params
|
|
raw_url_column = params[:url_column].presence || (parsed[:columns].length == 1 ? 0 : nil)
|
|
return render_bad_request('URL 列を選択してください.') if raw_url_column.nil?
|
|
|
|
url_column = Integer(raw_url_column, exception: false)
|
|
if url_column.nil? || url_column.negative? || url_column >= parsed[:columns].length
|
|
return render_bad_request('URL 列が不正です.')
|
|
end
|
|
|
|
rows = PostImportPreviewer.new(
|
|
parsed:,
|
|
url_column:,
|
|
mappings: mappings_params(parsed[:columns].length),
|
|
existing: params[:existing]).preview
|
|
render json: parsed.slice(:columns).merge(rows:)
|
|
rescue ArgumentError => e
|
|
render_bad_request(e.message)
|
|
end
|
|
|
|
def validate
|
|
rows = normalised_import_rows(allow_warning_fields: true)
|
|
changed_row = Integer(params[:changed_row], exception: false)
|
|
result =
|
|
PostImportPreviewer.new(
|
|
parsed: { columns: [], rows: [] },
|
|
url_column: 0,
|
|
mappings: {},
|
|
existing: params[:existing])
|
|
.preview_rows(rows:, fetch_metadata: changed_row, metadata_cache: {})
|
|
render json: { rows: result }
|
|
rescue ArgumentError => e
|
|
render_bad_request(e.message)
|
|
end
|
|
|
|
def create
|
|
result = PostImportRunner.new(
|
|
actor: current_user,
|
|
rows: normalised_import_rows,
|
|
existing: params[:existing]).run
|
|
render json: result, status: result[:created].positive? ? :created : :ok
|
|
rescue ArgumentError => e
|
|
render_bad_request(e.message)
|
|
end
|
|
|
|
private
|
|
|
|
def require_member!
|
|
return head :unauthorized unless current_user
|
|
return if current_user.gte_member?
|
|
|
|
head :forbidden
|
|
end
|
|
|
|
def parsed_params
|
|
value = params.require(:parsed).to_unsafe_h.deep_transform_keys { _1.to_s.underscore }
|
|
columns = Array(value['columns'])
|
|
rows = Array(value['rows'])
|
|
unless columns.all?(Hash) && rows.all?(Hash)
|
|
raise ArgumentError, '解析結果の形式が不正です.'
|
|
end
|
|
raise ArgumentError, '取込件数が多すぎます.' if rows.length > PostImportSourceParser::MAX_ROWS
|
|
raise ArgumentError, '列数が多すぎます.' if columns.length > 50
|
|
raise ArgumentError, '解析結果のセルが不正です.' unless rows.all? { |row|
|
|
values = Array(row['values'])
|
|
values.length <= columns.length && values.all? { |value|
|
|
value.is_a?(String) && value.bytesize <= PostImportSourceParser::MAX_CELL_BYTES
|
|
}
|
|
}
|
|
if rows.sum { Array(_1['values']).sum(&:bytesize) } > PostImportSourceParser::MAX_BYTES
|
|
raise ArgumentError, '解析結果が大きすぎます.'
|
|
end
|
|
|
|
parsed_rows = rows.map do |row|
|
|
source_row = Integer(row['source_row'], exception: false)
|
|
raise ArgumentError, '元行番号が不正です.' if source_row.nil? || source_row <= 0
|
|
|
|
{ source_row:, values: Array(row['values']) }
|
|
end
|
|
if parsed_rows.map { _1[:source_row] }.uniq.length != parsed_rows.length
|
|
raise ArgumentError, '元行番号が重複しています.'
|
|
end
|
|
|
|
{ columns: columns.map { |column| column.slice('key', 'label') }, rows: parsed_rows }
|
|
end
|
|
|
|
def mappings_params column_count
|
|
mappings = params[:mappings]
|
|
return { } if mappings.blank?
|
|
raise ArgumentError, '列割当ての形式が不正です.' unless mappings.is_a?(ActionController::Parameters)
|
|
|
|
raw = mappings.to_unsafe_h
|
|
unknown_fields = raw.keys - PostImportPreviewer::FIELDS
|
|
raise ArgumentError, '列割当て先が不正です.' if unknown_fields.present?
|
|
raise ArgumentError, '列割当てが大きすぎます.' if raw.to_json.bytesize > PostImportSourceParser::MAX_BYTES
|
|
|
|
permitted =
|
|
mappings.permit(*PostImportPreviewer::FIELDS.map { |field|
|
|
{ field => [:kind, :value, { columns: [] }] }
|
|
}).to_h
|
|
|
|
raw.each_with_object({ }) do |(field, value), result|
|
|
raise ArgumentError, '列割当ての形式が不正です.' unless value.is_a?(Hash)
|
|
unless (value.keys - MAPPING_PROPERTIES).empty?
|
|
raise ArgumentError, '列割当ての項目が不正です.'
|
|
end
|
|
|
|
value = permitted.fetch(field)
|
|
kind = value['kind'].presence || 'columns'
|
|
raise ArgumentError, '列割当ての種別が不正です.' unless kind.in?(MAPPING_KINDS)
|
|
columns = value['columns']
|
|
raise ArgumentError, '列番号が不正です.' unless (
|
|
columns.nil? || (columns.is_a?(Array) && columns.all? { |index|
|
|
parsed = Integer(index, exception: false)
|
|
parsed && parsed >= 0 && parsed < column_count
|
|
})
|
|
)
|
|
parsed_columns = Array(columns).map { Integer(_1) }
|
|
raise ArgumentError, '列番号が重複しています.' if parsed_columns.uniq.length != parsed_columns.length
|
|
raise ArgumentError, '固定値が不正です.' if kind == 'fixed' && !value['value'].is_a?(String)
|
|
if value['value'].to_s.bytesize > PostImportSourceParser::MAX_CELL_BYTES
|
|
raise ArgumentError, '固定値が大きすぎます.'
|
|
end
|
|
|
|
result[field] = {
|
|
'kind' => kind,
|
|
'value' => value['value'].to_s,
|
|
'columns' => parsed_columns }
|
|
end
|
|
end
|
|
|
|
def normalised_import_rows allow_warning_fields: false
|
|
rows = params[:rows]
|
|
PostImportRowNormaliser.normalise!(rows, allow_warning_fields:)
|
|
end
|
|
end
|