# Part 2: Importing all templates from Postmark

[cover image: Photo by [Maksim Goncharenok](https://www.pexels.com/@maksgelatin?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels) from [Pexels](https://www.pexels.com/photo/gold-letter-y-on-black-background-5605061/?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels)]
## Goal
As explained in [part one](https://blog.codemanship.dev/manage-postmark-emails-planning), we want to import all Postmark templates into our local system to be able to change them in bulk and export them to Postmark later. 

In this section, we will look at importing e-mails from Postmark. 

## Data Structure
E-mails in Postmark are not only composed of HTML. They also have: 
- A text body
- A layout id
- A name
- A subject
- An alias
- An associated server-id
- An active attribute
- A layout template

To both allow changing easily the HTML of the e-mails and the data associated with our e-mail, we decided to structure the data in two ways: 
- an HTML file per template named with the following pattern: #{template_name}_#{template_id}.
- a global metadata.yml file with the rest of the templates information. Each e-mail has one entry under its id and then an entry per attribute + the DateTime of the import for that template.

```YAML
1234:
  :name: template_name
  :subject: Some Subject
  :associated_server_id: 1564321
  :active: true
  :text_body: The text body
  :alias: some-alias
  :layout_template: yago_layout_nl
  :imported_at: "2022-02-09T15:53"
```

## Code
### Code Structure
We decided to implement an `ExternalTemplate` model that represents the data on Postmark. We felt that it would have different responsibilities than the `InternalTemplate` model, which would focus on representing the mail on our DB. 

To bulk import the e-mails, we also decided to implement a `TemplateGetter` service. The `TemplateGetter` service will make use of the `ExternalTemplate`.

![Untitled Diagram.drawio(6).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1647447845886/4fqXtI_08.png)

### ExternalTemplate
Its responsibilities are to get the data from Postmark and instantiate itself as an object (`initialize`), and `save` itself on the DB. For that, it will user `write_html_body` and `write_metadata`.

```Ruby
class ExternalTemplate
  def initialize(id, server_key)
    postmark = Postmark::ApiClient.new(server_key)
    postmark.get_template(id).each do |key, value|
      instance_variable_set("@#{key}", value)
      self.class.send(:attr_reader, key)
    end
  end

  def save
    print '.'
    write_html_body
    write_metadata
  end

  private

  def write_html_body
    File.open("imported_templates/#{name}_#{template_id}.html", 'w+') do |file|
      file << html_body
    end
  end

  def write_metadata
    require 'yaml'
    data = YAML.load_file('imported_templates/metadata.yml') || {}

    data[template_id] = yaml_attributes.merge(imported_at: Time.now.strftime('%Y-%m-%dT%H:%M'))
    File.open('imported_templates/metadata.yml', 'w+') do |file|
      YAML.dump(data, file)
    end
  end

  def yaml_attributes
    {
      name: name,
      subject: subject,
      associated_server_id: associated_server_id,
      active: active,
      text_body: text_body,
      alias: self.alias,
      layout_template: layout_template
    }
  end
end
```

### TemplateGetter

Its responsibilities are: 
- Iterate through the postmark servers (limited to 100 templates, so we currently have two servers)
- Get the ids of the server's templates
- Initialize an `InternalTemplate` for each id
- Tell the `InternalTemplate` to save

```Ruby
require 'rubygems'
require 'bundler/setup'
require 'yaml'
require 'postmark'
require_relative '../models/external_template'

class TemplateGetter
  def self.import_templates
    postmark_api_servers.each do |server_key|
      templates_ids(server_key).each { |id| download_template(id, server_key) }
    end
  end

  def self.postmark_api_servers
    secrets = YAML.load_file('secrets.yml')
    [secrets['postmark_api_server_1'], secrets['postmark_api_server_2']]
  end

  def self.templates_ids(server_key)
    postmark = Postmark::ApiClient.new(server_key)
    postmark.get_templates(count: 100)[1].map { |template| template[:template_id] }
  end

  def self.download_template(id, server_key)
    template = ExternalTemplate.new(id, server_key)
    template.save
  end
end
```

## Conclusion
Building a tool to import templates from Postmark is relatively easy (thanks to their [gem](https://github.com/wildbit/postmark-gem). If you find yourself having to make changes in bulk or even just wanting to have a copy of all your e-mail data on Postmark, this system is relatively easy to implement.

In our case, we added a CLI tool to be able to interact easily with the code: 

```Ruby
require_relative 'services/template_getter'

puts 'welcome to the Postmark cli tool'
puts 'would you like to: ?'
puts '1. import all e-mails'

input = gets

case input.strip
when '1'
  TemplateGetter.new.get_all_templates
else
  puts 'invalid input'
end
```

In Part 3 we will discuss the strategies we implemented to test our changes. 
 
