diff --git a/app/aspects/designs/importer.rb b/app/aspects/designs/importer.rb new file mode 100644 index 00000000..eab9756b --- /dev/null +++ b/app/aspects/designs/importer.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +require "core" +require "dry/monads" +require "initable" +require "pipeable" +require "yaml" + +module Terminus + module Aspects + module Designs + # Imports (creates) screen template from zip file. + class Importer + include Deps["aspects.unzipper", :logger, repository: "repositories.screen_template"] + include Initable[ + key_map: { + "configuration.yml" => :configuration, + "index.html.liquid" => :content + }, + problem: Aspects::Errors::Problem + ] + include Dry::Monads[:result] + include Pipeable + + def initialize( + schema: Schemas::Designs::Import, + error_joiner: Aspects::Errors::ResultJoiner, + ** + ) + @schema = schema + @error_joiner = error_joiner + super(**) + end + + def call io + process io + rescue ROM::SQL::UniqueConstraintError => error + Failure problem.duplicate(error.message, nil).detail + end + + private + + attr_reader :schema, :error_joiner + + def process io + pipe( + unzipper.call(io), + fmap { |entries| transform entries }, + validate(schema), + amap { error_joiner.call "Import", it }, + fmap { create it.to_h } + ) + end + + def transform entries + entries.transform_keys!(key_map).then { {**it, **YAML.load(it[:configuration])} } + end + + def create attributes + repository.create(attributes).tap { |screen_template| log screen_template } + end + + def log screen_template + logger.debug do + {tags: [{screen_template_id: screen_template.id}], message: "Imported design."} + end + end + end + end + end +end diff --git a/spec/app/aspects/designs/importer_spec.rb b/spec/app/aspects/designs/importer_spec.rb new file mode 100644 index 00000000..91e1dd31 --- /dev/null +++ b/spec/app/aspects/designs/importer_spec.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require "hanami_helper" + +RSpec.describe Terminus::Aspects::Designs::Importer, :db do + subject(:creator) { described_class.new } + + describe "#call" do + let :io do + manifest = {"configuration.yml" => configuration, "index.html.liquid" => "