Files
warp_engine/app/dtos/warp_engine/upload_input_dto.rb
T
mr.zeroandClaude Opus 5 7c9f2988be The build endpoints state their input rules once
`POST /build/publish` opened with three guard clauses and `POST /build/upload`
with eight, each one a `return render json: { error: ... }` — name present,
version present, name format, version format, file present, filename prefix,
size, digest. That is a validation layer written by hand, in a place where it
cannot be unit tested: exercising it needs a request.

`PublishInputDto` was already there and was a bare `Struct` with no rules at
all, so the controller carried them. It is an `ActiveModel::Model` now, with
the presence and platform-inclusion validations on it, and `UploadInputDto`
joins it with the name and version formats and the `<name>-<version>` filename
convention. Each controller reads:

    return render json: { error: input.error_message }, status: :bad_request unless input.valid?

Size and digest keep their own explicit checks, because they are not the same
answer: 413 tells a caller to stop, 422 tells it to retry a truncated upload,
and a single error bag cannot say which. Every status code the endpoints
answered before, they answer now — build_publish_controller_spec and
build_uploads_controller_spec pin all of them, unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-23 09:01:10 +02:00

38 lines
948 B
Ruby

module WarpEngine
class UploadInputDto
include ActiveModel::Model
NAME_FORMAT = /\A[A-Za-z0-9._-]+\z/
attr_accessor :name, :version, :file, :sha256
validates :name, format: { with: NAME_FORMAT }
validates :version, format: { with: NAME_FORMAT }
validate :file_present
validate :filename_prefixed
def filename
return nil unless file.respond_to?(:original_filename)
File.basename(file.original_filename.to_s)
end
def error_message = errors.full_messages.to_sentence
private
def file_present
return if file.respond_to?(:original_filename)
errors.add(:file, "not provided")
end
def filename_prefixed
return if filename.nil? || errors.include?(:name) || errors.include?(:version)
return if filename.start_with?("#{name}-#{version}.", "#{name}-#{version}-")
errors.add(:file, "name must be prefixed with #{name}-#{version}")
end
end
end