mailerlite-ruby 1.0.6 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +2 -0
  3. data/.github/workflows/main.yml +4 -4
  4. data/.github/workflows/publish_gem.yml +5 -5
  5. data/.gitignore +2 -1
  6. data/.rubocop.yml +1 -1
  7. data/CHANGELOG.md +21 -0
  8. data/README.md +583 -1
  9. data/flake.lock +27 -0
  10. data/flake.nix +25 -0
  11. data/lefthook.yml +14 -0
  12. data/lib/mailerlite/api_response.rb +24 -0
  13. data/lib/mailerlite/automations/automation_emails.rb +57 -0
  14. data/lib/mailerlite/automations/automation_reports.rb +114 -0
  15. data/lib/mailerlite/automations/automation_steps.rb +105 -0
  16. data/lib/mailerlite/automations/automation_templates.rb +34 -0
  17. data/lib/mailerlite/automations/automation_triggers.rb +62 -0
  18. data/lib/mailerlite/automations/automations.rb +177 -8
  19. data/lib/mailerlite/brand_styles/brand_styles.rb +24 -0
  20. data/lib/mailerlite/campaigns/campaigns.rb +33 -1
  21. data/lib/mailerlite/client.rb +15 -5
  22. data/lib/mailerlite/ecommerce/ecommerce.rb +316 -0
  23. data/lib/mailerlite/emails/emails.rb +306 -0
  24. data/lib/mailerlite/errors.rb +88 -0
  25. data/lib/mailerlite/files/files.rb +45 -0
  26. data/lib/mailerlite/groups/groups.rb +16 -8
  27. data/lib/mailerlite/params.rb +58 -0
  28. data/lib/mailerlite/query_params.rb +33 -0
  29. data/lib/mailerlite/request.rb +24 -0
  30. data/lib/mailerlite/segments/segments.rb +10 -10
  31. data/lib/mailerlite/subscribers/subscribers.rb +63 -8
  32. data/lib/mailerlite/version.rb +1 -1
  33. data/lib/mailerlite.rb +14 -0
  34. data/mailerlite-ruby.gemspec +8 -8
  35. data/spec/api_response_rspec.rb +140 -0
  36. data/spec/automation_emails_rspec.rb +40 -0
  37. data/spec/automation_reports_rspec.rb +92 -0
  38. data/spec/automation_steps_rspec.rb +76 -0
  39. data/spec/automation_templates_rspec.rb +24 -0
  40. data/spec/automation_triggers_rspec.rb +48 -0
  41. data/spec/automations_rspec.rb +143 -0
  42. data/spec/brand_styles_rspec.rb +49 -0
  43. data/spec/campaigns_rspec.rb +42 -0
  44. data/spec/ecommerce_rspec.rb +141 -0
  45. data/spec/emails_rspec.rb +692 -0
  46. data/spec/files_rspec.rb +90 -0
  47. data/spec/fixtures/brand_styles.json +50 -0
  48. data/spec/fixtures/campaign_simple_editor.json +24 -0
  49. data/spec/fixtures/email.json +81 -0
  50. data/spec/fixtures/email_block.json +12 -0
  51. data/spec/fixtures/email_block_created.json +12 -0
  52. data/spec/fixtures/email_block_created_rejected.json +15 -0
  53. data/spec/fixtures/email_block_moved.json +12 -0
  54. data/spec/fixtures/email_block_updated.json +9 -0
  55. data/spec/fixtures/email_block_updated_rejected.json +13 -0
  56. data/spec/fixtures/email_block_validation_invalid.json +5 -0
  57. data/spec/fixtures/email_etag.json +1 -0
  58. data/spec/fixtures/email_html.json +1 -0
  59. data/spec/fixtures/email_settings.json +111 -0
  60. data/spec/fixtures/email_settings_validation.json +9 -0
  61. data/spec/fixtures/email_template.json +1 -0
  62. data/spec/fixtures/email_version.json +1 -0
  63. data/spec/fixtures/email_versions.json +26 -0
  64. data/spec/fixtures/files.json +44 -0
  65. data/spec/groups_rspec.rb +21 -0
  66. data/spec/params_rspec.rb +126 -0
  67. data/spec/request_rspec.rb +139 -0
  68. data/spec/segments_rspec.rb +39 -0
  69. data/spec/spec_helper.rb +1 -0
  70. data/spec/subscribers_rspec.rb +57 -0
  71. data/spec/support/json_fixture.rb +27 -0
  72. data/spec/support/request_stub.rb +25 -0
  73. metadata +69 -22
  74. /data/{renovate.json → .github/renovate.json} +0 -0
data/README.md CHANGED
@@ -55,11 +55,32 @@ For more information how to obtain an API key visit the [following link](https:/
55
55
  - [List all campaigns](#list-all-campaigns)
56
56
  - [Get a campaign](#get-a-campaign)
57
57
  - [Create a campaign](#create-a-campaign)
58
+ - [Create a campaign with the simple editor](#create-a-campaign-with-the-simple-editor)
58
59
  - [Update a campaign](#update-a-campaign)
59
60
  - [Schedule a campaign](#schedule-a-campaign)
60
61
  - [Cancel a campaign](#cancel-a-campaign)
61
62
  - [Delete a campaign](#delete-a-campaign)
62
63
  - [Get subscribers activity for a campaign](#get-subscribers-activity-for-a-campaign)
64
+ - [Emails](#emails)
65
+ - [Get an email](#get-an-email)
66
+ - [Get the HTML of an email](#get-the-html-of-an-email)
67
+ - [Get a block](#get-a-block)
68
+ - [Insert a block](#insert-a-block)
69
+ - [Update a block](#update-a-block)
70
+ - [Delete a block](#delete-a-block)
71
+ - [Move a block](#move-a-block)
72
+ - [Validate a block](#validate-a-block)
73
+ - [Update the design settings](#update-the-design-settings)
74
+ - [Validate the design settings](#validate-the-design-settings)
75
+ - [Save an email as a template](#save-an-email-as-a-template)
76
+ - [List the versions of an email](#list-the-versions-of-an-email)
77
+ - [Save a version of an email](#save-a-version-of-an-email)
78
+ - [Restore a version of an email](#restore-a-version-of-an-email)
79
+ - [Retry a write with an idempotency key](#retry-a-write-with-an-idempotency-key)
80
+ - [Brand styles](#brand-styles)
81
+ - [Get the brand styles](#get-the-brand-styles)
82
+ - [Files](#files)
83
+ - [List all files](#list-all-files)
63
84
  - [Forms](#forms)
64
85
  - [List all forms](#list-all-forms)
65
86
  - [Get a form](#get-a-form)
@@ -78,6 +99,13 @@ For more information how to obtain an API key visit the [following link](https:/
78
99
  - [Get a list of timezones](#get-a-list-of-timezones)
79
100
  - [Campaign languages](#campaign-languages)
80
101
  - [Get a list of languages](#get-a-list-of-languages)
102
+ - [E-commerce](#e-commerce)
103
+ - [Shops](#shops)
104
+ - [Products and variants](#products-and-variants)
105
+ - [Categories](#categories)
106
+ - [Customers, orders and carts](#customers-orders-and-carts)
107
+ - [Imports](#imports)
108
+ - [Revenue reports and settings](#revenue-reports-and-settings)
81
109
 
82
110
  ## Setup
83
111
 
@@ -89,7 +117,15 @@ You will have to initalize it in your Ruby file with `require "mailerlite-ruby"`
89
117
 
90
118
  # Usage
91
119
 
92
- This SDK requires that you either have `.env` file with `MAILERLITE_API_TOKEN` env variable or that your variable is enabled system wide (useful for Docker/Kubernetes). The example of how `MAILERLITE_API_TOKEN` should look like is in `.env.example`.
120
+ This SDK requires that you have the `MAILERLITE_API_TOKEN` environment variable set. You can set this variable in a `.env` file or enable it system-wide (useful for Docker/Kubernetes). The example of how `MAILERLITE_API_TOKEN` should look like is in `.env.example`.
121
+
122
+ If you want to use `dotenv` to manage your environment variables, you can configure the `mailerlite` gem to load `dotenv`:
123
+
124
+ ```ruby
125
+ MailerLite.configure do |config|
126
+ config.use_dotenv = true
127
+ end
128
+ ```
93
129
 
94
130
  ## Subscribers
95
131
 
@@ -472,6 +508,36 @@ campaigns.create(
472
508
  )
473
509
  ```
474
510
 
511
+ ### Create a campaign with the simple editor
512
+
513
+ <a name="create-a-campaign-with-the-simple-editor"></a>
514
+
515
+ To build the email with the simple editor, set `editor` to `MailerLite::Campaigns::EDITOR_SIMPLE`, and do not send `content`. Then edit the email with [the email methods](#emails).
516
+
517
+ - `template_id` is optional. It is the simple editor template that the email starts from: a template from the shared gallery, or a template that you saved from your own email with [`create_template`](#save-an-email-as-a-template) (the `id` in its response). It needs `editor`. Without it, the email starts from the default blank template. The API copies the template into the new email and does not change the template.
518
+ - All emails of a campaign must have the same `editor` and `template_id`.
519
+ - Only `create` accepts `editor` and `template_id`. `update` does not.
520
+
521
+ ```ruby
522
+ require "mailerlite-ruby"
523
+
524
+ # Initialize the class
525
+ campaigns = MailerLite::Campaigns.new
526
+
527
+ response = campaigns.create(
528
+ name: "Test Campaign",
529
+ type: "regular",
530
+ emails: [{
531
+ subject: "This is a test campaign",
532
+ from_name: "Test Man",
533
+ from: "testuser@mailerlite.com",
534
+ editor: MailerLite::Campaigns::EDITOR_SIMPLE
535
+ }]
536
+ )
537
+
538
+ email_id = JSON.parse(response.body)['data']['emails'][0]['id']
539
+ ```
540
+
475
541
  ### Update a campaign
476
542
 
477
543
  <a name="update-a-campaign"></a>
@@ -561,6 +627,408 @@ campaign_id = 123456
561
627
  campaigns.activity(campaign_id)
562
628
  ```
563
629
 
630
+ ## Emails
631
+
632
+ <a name="emails"></a>
633
+
634
+ These methods edit emails built with the simple editor. To create such an email, [create a campaign with the simple editor](#create-a-campaign-with-the-simple-editor). The ID of the email is in `data.emails[0].id` of the response.
635
+
636
+ - IDs are strings of digits. You can also give them as integers. An empty ID raises `MailerLite::EmptyIdError`, and an ID that does not contain only digits raises `MailerLite::InvalidIdError`. The SDK sends no request in these cases.
637
+ - The SDK checks only the IDs in the URL path, and the settings of `update_settings`: they must be a Hash without an `etag` key. The SDK sends all other values as you give them, and the API checks them. For a value that is not valid, the API returns `422` with the `errors` for each field.
638
+ - The methods return an `HTTP::Response`, like the other methods. Use `response.code` for the status code, `response.headers` for the headers and `JSON.parse(response.body)` for the body.
639
+ - The methods do not raise an error for a `4xx` or `5xx` status. `response.api_error` is `nil` for a `2xx` status. For other statuses, it is a `MailerLite::ApiError` with `status`, `code`, `etag`, `api_message` and `errors`. You can raise it.
640
+ - Each write needs the etag from the last response for the email, and returns a new etag. The etag covers the whole email, so send the writes to one email one after another.
641
+ - If the email changed after you read it, the write returns `409`, and `response.api_error.code` is `MailerLite::Emails::VERSION_CONFLICT`. Then `response.api_error.etag` is the current etag. Get the email again, and then send the write again with the current etag.
642
+ - The keys of `editor_content` are camelCase, for example `textAlign`. The SDK sends them as you give them.
643
+
644
+ ### Get an email
645
+
646
+ <a name="get-an-email"></a>
647
+
648
+ Returns the email with its settings and blocks, and the etag of the email.
649
+
650
+ ```ruby
651
+ require "mailerlite-ruby"
652
+
653
+ # Initialize the class
654
+ emails = MailerLite::Emails.new
655
+
656
+ response = emails.get('123456')
657
+
658
+ body = JSON.parse(response.body)
659
+ email = body['data']
660
+ etag = body['etag']
661
+ ```
662
+
663
+ ### Get the HTML of an email
664
+
665
+ <a name="get-the-html-of-an-email"></a>
666
+
667
+ Returns the HTML that the email sends. The API can return a temporary `503` error. If it does, try again later.
668
+
669
+ ```ruby
670
+ require "mailerlite-ruby"
671
+
672
+ # Initialize the class
673
+ emails = MailerLite::Emails.new
674
+
675
+ response = emails.get_html('123456')
676
+
677
+ html = JSON.parse(response.body)['data']['html'] if response.code == 200
678
+ ```
679
+
680
+ ### Get a block
681
+
682
+ <a name="get-a-block"></a>
683
+
684
+ Returns the block and its place in the email: `placement` has the IDs of the blocks `after` and `before` it, or `nil`.
685
+
686
+ ```ruby
687
+ require "mailerlite-ruby"
688
+
689
+ # Initialize the class
690
+ emails = MailerLite::Emails.new
691
+
692
+ response = emails.get_block(email_id: '123456', block_id: '57')
693
+
694
+ block = JSON.parse(response.body)['data']
695
+ ```
696
+
697
+ ### Insert a block
698
+
699
+ <a name="insert-a-block"></a>
700
+
701
+ Use the block types in `MailerLite::Emails::BlockType`. `editor_content` can be empty. Without `placement`, the API puts the block at the end of the email. The positions are in `MailerLite::Emails::BlockPosition`. The `BEFORE` and `AFTER` positions also need a `block_id`. The SDK sends `placement` as you give it.
702
+
703
+ The API drops the attributes and the `mobile` keys that the block type does not declare, and the write still succeeds. Then the response has `rejected` with a `message` and the `keys` that the API dropped, for example `editor_content.attrs.onclick`. When the API drops nothing, the response has no `rejected`.
704
+
705
+ ```ruby
706
+ require "mailerlite-ruby"
707
+
708
+ # Initialize the class
709
+ emails = MailerLite::Emails.new
710
+
711
+ email_id = '123456'
712
+ etag = JSON.parse(emails.get(email_id).body)['etag']
713
+
714
+ response = emails.create_block(
715
+ email_id: email_id,
716
+ etag: etag,
717
+ type: MailerLite::Emails::BlockType::PARAGRAPH,
718
+ editor_content: { attrs: { textAlign: 'left' }, content: [{ type: 'text', text: 'Hello' }] },
719
+ placement: { position: MailerLite::Emails::BlockPosition::AT_START }
720
+ )
721
+
722
+ body = JSON.parse(response.body)
723
+ block_id = body['data']['id']
724
+ etag = body['etag']
725
+ puts body['rejected']['keys'] if body['rejected']
726
+ ```
727
+
728
+ ### Update a block
729
+
730
+ <a name="update-a-block"></a>
731
+
732
+ The new `editor_content` replaces the whole content of the block. The API removes the keys that you do not send.
733
+
734
+ The API drops the attributes and the `mobile` keys that the block type does not declare, and the write still succeeds. Then the response has `rejected` with a `message` and the `keys` that the API dropped, for example `editor_content.mobile.onclick`. When the API drops nothing, the response has no `rejected`.
735
+
736
+ ```ruby
737
+ require "mailerlite-ruby"
738
+
739
+ # Initialize the class
740
+ emails = MailerLite::Emails.new
741
+
742
+ response = emails.update_block(
743
+ email_id: '123456',
744
+ block_id: '57',
745
+ etag: 'etag-from-the-last-response',
746
+ editor_content: { attrs: { textAlign: 'center' }, content: [{ type: 'text', text: 'Hello again' }] }
747
+ )
748
+
749
+ body = JSON.parse(response.body)
750
+ etag = body['etag']
751
+ puts body['rejected']['keys'] if body['rejected']
752
+ ```
753
+
754
+ ### Delete a block
755
+
756
+ <a name="delete-a-block"></a>
757
+
758
+ Also deletes the blocks in it. The response has only the new etag.
759
+
760
+ ```ruby
761
+ require "mailerlite-ruby"
762
+
763
+ # Initialize the class
764
+ emails = MailerLite::Emails.new
765
+
766
+ response = emails.delete_block(email_id: '123456', block_id: '57', etag: 'etag-from-the-last-response')
767
+
768
+ etag = JSON.parse(response.body)['etag']
769
+ ```
770
+
771
+ ### Move a block
772
+
773
+ <a name="move-a-block"></a>
774
+
775
+ The `BEFORE` and `AFTER` positions also need a `block_id`. The SDK sends `placement` as you give it.
776
+
777
+ ```ruby
778
+ require "mailerlite-ruby"
779
+
780
+ # Initialize the class
781
+ emails = MailerLite::Emails.new
782
+
783
+ response = emails.move_block(
784
+ email_id: '123456',
785
+ block_id: '57',
786
+ etag: 'etag-from-the-last-response',
787
+ placement: { position: MailerLite::Emails::BlockPosition::AFTER, block_id: '12' }
788
+ )
789
+
790
+ etag = JSON.parse(response.body)['etag']
791
+ ```
792
+
793
+ ### Validate a block
794
+
795
+ <a name="validate-a-block"></a>
796
+
797
+ Checks a block before you insert it. It needs no email and no etag. An invalid block is not an error: the response has `valid` set to `false` and the `errors` for each field.
798
+
799
+ If a valid block has attributes or `mobile` keys that the block type does not declare, the response has `rejected` with a `message` and these `keys`. Insert and update drop these keys. When `valid` is `false`, the response has no `rejected`.
800
+
801
+ The block in this example is not valid, because `sideways` is not a valid `textAlign`. The response has `valid: false` and `errors`.
802
+
803
+ ```ruby
804
+ require "mailerlite-ruby"
805
+
806
+ # Initialize the class
807
+ emails = MailerLite::Emails.new
808
+
809
+ response = emails.validate_block(
810
+ type: MailerLite::Emails::BlockType::PARAGRAPH,
811
+ editor_content: { attrs: { textAlign: 'sideways' } }
812
+ )
813
+
814
+ body = JSON.parse(response.body)
815
+ puts body['errors'] unless body['valid']
816
+ ```
817
+
818
+ ### Update the design settings
819
+
820
+ <a name="update-the-design-settings"></a>
821
+
822
+ Send only the keys to change, in the shape of `settings` in the email. The groups are `brand`, `layout`, `typography`, `button` and `footer`.
823
+
824
+ - The SDK sends exactly the keys of the Hash. A key that is not in the Hash does not change.
825
+ - To unset a brand colour, send an empty string. The API stores the value empty, and the email then uses the account value.
826
+ - To unset a footer text, send an empty editor document: `{ editor_content: {} }` or `{ editor_content: { content: [] } }`.
827
+ - Other colours, sizes, `button.border_radius` and `brand.style` cannot be empty. An empty string gives a `422` error.
828
+ - Send all style values as strings, for example `'16'` and not `16`.
829
+ - The API stores each value that you send, also a value that the email inherits from your account. Then the email no longer follows your account for that value.
830
+ - `footer.social_links` replaces the whole list. Keep the `link_id` of each link to keep its click history.
831
+ - `brand.mode` is one of `MailerLite::Emails::BrandMode`. To apply a preset palette, send its `identifier` from the brand styles in `brand.style`.
832
+ - Do not put `etag` in the settings. Use the `etag` argument.
833
+ - If the API removed unsafe values, the response has `rejected` with the keys of these values.
834
+
835
+ ```ruby
836
+ require "mailerlite-ruby"
837
+
838
+ # Initialize the class
839
+ emails = MailerLite::Emails.new
840
+
841
+ response = emails.update_settings(
842
+ email_id: '123456',
843
+ etag: 'etag-from-the-last-response',
844
+ settings: {
845
+ brand: { mode: MailerLite::Emails::BrandMode::CUSTOM, background_color: '#f4f4f4' },
846
+ typography: { paragraph: { p1: { size: '18' } } }
847
+ }
848
+ )
849
+
850
+ body = JSON.parse(response.body)
851
+ etag = body['etag']
852
+ puts body['rejected']['keys'] if body['rejected']
853
+ ```
854
+
855
+ ### Validate the design settings
856
+
857
+ <a name="validate-the-design-settings"></a>
858
+
859
+ Checks a change with the rules of the update, and saves nothing. It needs no etag. Invalid settings are not an error: the response has `valid` set to `false` and the `errors` for each field.
860
+
861
+ ```ruby
862
+ require "mailerlite-ruby"
863
+
864
+ # Initialize the class
865
+ emails = MailerLite::Emails.new
866
+
867
+ response = emails.validate_settings(email_id: '123456', settings: { button: { border_radius: '8' } })
868
+
869
+ body = JSON.parse(response.body)
870
+ puts body['errors'] unless body['valid']
871
+ ```
872
+
873
+ ### Save an email as a template
874
+
875
+ <a name="save-an-email-as-a-template"></a>
876
+
877
+ Copies the email to a new template. The API cannot delete templates.
878
+
879
+ ```ruby
880
+ require "mailerlite-ruby"
881
+
882
+ # Initialize the class
883
+ emails = MailerLite::Emails.new
884
+
885
+ response = emails.create_template(email_id: '123456', name: 'Monthly newsletter')
886
+
887
+ template_id = JSON.parse(response.body)['data']['id']
888
+ ```
889
+
890
+ ### List the versions of an email
891
+
892
+ <a name="list-the-versions-of-an-email"></a>
893
+
894
+ A version is a saved copy of the content and the settings of an email. The list starts with the newest version. `current` marks the version that the email has now.
895
+
896
+ ```ruby
897
+ require "mailerlite-ruby"
898
+
899
+ # Initialize the class
900
+ emails = MailerLite::Emails.new
901
+
902
+ response = emails.list_versions('123456', page: 1, limit: 10)
903
+
904
+ versions = JSON.parse(response.body)['data']
905
+ ```
906
+
907
+ ### Save a version of an email
908
+
909
+ <a name="save-a-version-of-an-email"></a>
910
+
911
+ Writes through the API do not save versions. If nothing changed after the last version, the API saves no new version and returns the last version. `response.code` is `201` for a new version and `200` for the last version.
912
+
913
+ ```ruby
914
+ require "mailerlite-ruby"
915
+
916
+ # Initialize the class
917
+ emails = MailerLite::Emails.new
918
+
919
+ response = emails.create_version('123456')
920
+
921
+ version_id = JSON.parse(response.body)['data']['id']
922
+ is_new = response.code == 201
923
+ ```
924
+
925
+ ### Restore a version of an email
926
+
927
+ <a name="restore-a-version-of-an-email"></a>
928
+
929
+ Puts the email back to the version, and returns the full email with the new etag. Before the restore, the API saves the current state as a version, so you can undo the restore.
930
+
931
+ ```ruby
932
+ require "mailerlite-ruby"
933
+
934
+ # Initialize the class
935
+ emails = MailerLite::Emails.new
936
+
937
+ response = emails.restore_version(email_id: '123456', version_id: '901', etag: 'etag-from-the-last-response')
938
+
939
+ etag = JSON.parse(response.body)['etag']
940
+ ```
941
+
942
+ ### Retry a write with an idempotency key
943
+
944
+ <a name="retry-a-write-with-an-idempotency-key"></a>
945
+
946
+ If a write fails with a network error, you do not know if the API did the write. Give the write an `idempotency_key`, and send it again with the same key and the same arguments. If the first request was a success, the API returns the stored response and does not do the write two times. Then `response.idempotent_replay?` is `true`.
947
+
948
+ - Use a new key for each write. The API keeps a key for 24 hours.
949
+ - The key must be 1 to 255 printable ASCII characters. The SDK sends the key as you give it. For a key that is not valid, the API returns `400`.
950
+ - The same key with different arguments returns `422`. If the first request is still running, the API returns `409` without a `code`.
951
+ - These methods accept a key: `create_block`, `update_block`, `delete_block`, `move_block`, `update_settings`, `create_template`, `create_version` and `restore_version`.
952
+
953
+ ```ruby
954
+ require "mailerlite-ruby"
955
+ require "securerandom"
956
+
957
+ # Initialize the class
958
+ emails = MailerLite::Emails.new
959
+
960
+ args = {
961
+ email_id: '123456',
962
+ etag: 'etag-from-the-last-response',
963
+ type: MailerLite::Emails::BlockType::DIVIDER,
964
+ editor_content: {},
965
+ idempotency_key: SecureRandom.uuid
966
+ }
967
+
968
+ begin
969
+ response = emails.create_block(**args)
970
+ rescue HTTP::ConnectionError, HTTP::TimeoutError
971
+ # Network error: send the write again with the same key and the same arguments.
972
+ response = emails.create_block(**args)
973
+ end
974
+
975
+ error = response.api_error
976
+ raise error if error
977
+
978
+ puts JSON.parse(response.body)['data']['id'], response.idempotent_replay?
979
+ ```
980
+
981
+ ## Brand styles
982
+
983
+ <a name="brand-styles"></a>
984
+
985
+ ### Get the brand styles
986
+
987
+ <a name="get-the-brand-styles"></a>
988
+
989
+ Returns the palettes of your account: `brand` (your brand styles, or `nil` if your account has none), `default` and the `presets`. Match a palette on its `identifier`, not on its `name`. To apply a preset to an email, [update the design settings](#update-the-design-settings) with `{ brand: { style: identifier } }`.
990
+
991
+ ```ruby
992
+ require "mailerlite-ruby"
993
+
994
+ # Initialize the class
995
+ brand_styles = MailerLite::BrandStyles.new
996
+
997
+ response = brand_styles.get
998
+
999
+ identifiers = JSON.parse(response.body)['data']['presets'].map { |preset| preset['identifier'] }
1000
+ ```
1001
+
1002
+ ## Files
1003
+
1004
+ <a name="files"></a>
1005
+
1006
+ ### List all files
1007
+
1008
+ <a name="list-all-files"></a>
1009
+
1010
+ Returns the files of your media library. Use their `url` in image blocks. `width` and `height` are `nil` for files that are not images.
1011
+
1012
+ - `filter`: `name` (a part of the file name) and `type` (one of `MailerLite::Files::FileType`).
1013
+ - `sort`: one of `MailerLite::Files::FileSort`. The default is newest first.
1014
+ - The SDK does not send `nil` values.
1015
+
1016
+ ```ruby
1017
+ require "mailerlite-ruby"
1018
+
1019
+ # Initialize the class
1020
+ files = MailerLite::Files.new
1021
+
1022
+ response = files.list(
1023
+ filter: { name: 'summer sale', type: MailerLite::Files::FileType::IMAGE },
1024
+ sort: MailerLite::Files::FileSort::SIZE_DESC,
1025
+ page: 1,
1026
+ limit: 25
1027
+ )
1028
+
1029
+ urls = JSON.parse(response.body)['data'].map { |file| file['url'] }
1030
+ ```
1031
+
564
1032
  ## Forms
565
1033
 
566
1034
  <a name="forms"></a>
@@ -781,6 +1249,120 @@ campaigns = MailerLite::Campaigns.new
781
1249
  campaigns.languages()
782
1250
  ```
783
1251
 
1252
+ ## E-commerce
1253
+
1254
+ <a name="e-commerce"></a>
1255
+
1256
+ ### Shops
1257
+
1258
+ ```ruby
1259
+ require "mailerlite-ruby"
1260
+
1261
+ shops = MailerLite::Ecommerce::Shops.new
1262
+
1263
+ shops.list(limit: 25)
1264
+ shops.get('shop_id')
1265
+ shops.create({ name: 'My shop', url: 'https://shop.example.com', currency: 'EUR' })
1266
+ shops.update(shop_id: 'shop_id', params: { name: 'New name' })
1267
+ shops.delete('shop_id')
1268
+ ```
1269
+
1270
+ ### Products and variants
1271
+
1272
+ ```ruby
1273
+ products = MailerLite::Ecommerce::Products.new
1274
+
1275
+ products.list(shop_id: 'shop_id', filter: { enabled: true }, sort: '-created_at')
1276
+ products.get(shop_id: 'shop_id', product_id: 'product_id')
1277
+ products.create(shop_id: 'shop_id', params: { name: 'Red T-shirt', price: 19.99 })
1278
+ products.update(shop_id: 'shop_id', product_id: 'product_id', params: { price: 24.99 })
1279
+ products.delete(shop_id: 'shop_id', product_id: 'product_id')
1280
+ products.change_status(shop_id: 'shop_id', product_id: 'product_id', enabled: false)
1281
+ products.assign_categories(shop_id: 'shop_id', product_id: 'product_id', category_ids: ['category_id'], replace: true)
1282
+
1283
+ variants = MailerLite::Ecommerce::ProductVariants.new
1284
+
1285
+ variants.list(shop_id: 'shop_id', product_id: 'product_id')
1286
+ variants.sync(shop_id: 'shop_id', product_id: 'product_id', variants: [{ name: 'Large', attributes: { size: 'L' }, resource_id: 'variant-l' }])
1287
+ variants.update(shop_id: 'shop_id', product_id: 'product_id', variant_id: 'variant_id', params: { name: 'Extra large', attributes: { size: 'XL' } })
1288
+ variants.delete(shop_id: 'shop_id', product_id: 'product_id', variant_id: 'variant_id')
1289
+ ```
1290
+
1291
+ ### Categories
1292
+
1293
+ ```ruby
1294
+ categories = MailerLite::Ecommerce::Categories.new
1295
+
1296
+ categories.list(shop_id: 'shop_id')
1297
+ categories.get(shop_id: 'shop_id', category_id: 'category_id')
1298
+ categories.create(shop_id: 'shop_id', params: { name: 'Hats' })
1299
+ categories.update(shop_id: 'shop_id', category_id: 'category_id', params: { name: 'Caps' })
1300
+ categories.delete(shop_id: 'shop_id', category_id: 'category_id')
1301
+ categories.products(shop_id: 'shop_id', category_id: 'category_id')
1302
+ categories.assign_product(shop_id: 'shop_id', category_id: 'category_id', product_id: 'product_id')
1303
+ categories.unassign_product(shop_id: 'shop_id', category_id: 'category_id', product_id: 'product_id')
1304
+ ```
1305
+
1306
+ ### Customers, orders and carts
1307
+
1308
+ ```ruby
1309
+ customers = MailerLite::Ecommerce::Customers.new
1310
+ customers.list(shop_id: 'shop_id', filter: { email: 'customer@example.com' })
1311
+ customers.create(shop_id: 'shop_id', params: { email: 'customer@example.com', create_subscriber: true })
1312
+ customers.update(shop_id: 'shop_id', customer_id: 'customer_id', params: { name: 'Jane' })
1313
+ customers.delete(shop_id: 'shop_id', customer_id: 'customer_id')
1314
+
1315
+ orders = MailerLite::Ecommerce::Orders.new
1316
+ orders.list(shop_id: 'shop_id', filter: { attributed: true, email_id: 'email_id' })
1317
+ orders.create(shop_id: 'shop_id', params: {
1318
+ customer: { email: 'customer@example.com' },
1319
+ cart: { items: [{ ecommerce_product_id: 'product_id', quantity: 1, price: 19.99 }] },
1320
+ status: 'complete'
1321
+ })
1322
+ orders.update(shop_id: 'shop_id', order_id: 'order_id', params: { status: 'complete' })
1323
+ orders.delete(shop_id: 'shop_id', order_id: 'order_id')
1324
+
1325
+ carts = MailerLite::Ecommerce::Carts.new
1326
+ carts.list(shop_id: 'shop_id')
1327
+ carts.update(shop_id: 'shop_id', cart_id: 'cart_id', params: { checkout_url: 'https://shop.example.com/cart' })
1328
+
1329
+ cart_items = MailerLite::Ecommerce::CartItems.new
1330
+ cart_items.list(shop_id: 'shop_id', cart_id: 'cart_id')
1331
+ cart_items.update(shop_id: 'shop_id', cart_id: 'cart_id', item_id: 'item_id', params: { quantity: 2 })
1332
+ cart_items.delete(shop_id: 'shop_id', cart_id: 'cart_id', item_id: 'item_id')
1333
+ ```
1334
+
1335
+ ### Imports
1336
+
1337
+ ```ruby
1338
+ imports = MailerLite::Ecommerce::Imports.new
1339
+
1340
+ imports.products(shop_id: 'shop_id', products: [{ name: 'Red T-shirt', price: 19.99 }])
1341
+ imports.categories(shop_id: 'shop_id', categories: [{ name: 'Hats' }])
1342
+ imports.customers(shop_id: 'shop_id', customers: [{ email: 'customer@example.com' }])
1343
+ ```
1344
+
1345
+ ### Revenue reports and settings
1346
+
1347
+ ```ruby
1348
+ MailerLite::Ecommerce::Shops.new.stats(shop_id: 'shop_id', start_date: '2026-10-01', end_date: '2026-10-31')
1349
+
1350
+ campaigns = MailerLite::Campaigns.new
1351
+ campaigns.ecommerce_stats(campaign_id: 'campaign_id', shop_id: 'shop_id')
1352
+ campaigns.ecommerce_orders(campaign_id: 'campaign_id', shop_id: 'shop_id')
1353
+
1354
+ subscribers = MailerLite::Subscribers.new
1355
+ subscribers.ecommerce_stats(subscriber_id: 'subscriber_id', shop_id: 'shop_id')
1356
+ subscribers.ecommerce_orders(subscriber_id: 'subscriber_id', shop_id: 'shop_id')
1357
+
1358
+ automations = MailerLite::Automations.new
1359
+ automations.ecommerce_stats(automation_id: 'automation_id')
1360
+ automations.email_ecommerce_stats(automation_id: 'automation_id', email_id: 'email_id')
1361
+ automations.ecommerce_orders(automation_id: 'automation_id', time_frame: 'all_time')
1362
+
1363
+ MailerLite::Ecommerce::Settings.new.get
1364
+ ```
1365
+
784
1366
  # Testing
785
1367
 
786
1368
  ```bash
data/flake.lock ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "nodes": {
3
+ "nixpkgs": {
4
+ "locked": {
5
+ "lastModified": 1789546076,
6
+ "narHash": "sha256-zVxLZiSnmaaPLwnhj7pwmqe3axBg/C6nG5JZsJMh2g4=",
7
+ "owner": "NixOS",
8
+ "repo": "nixpkgs",
9
+ "rev": "b1b875982b17dabde9b4a37f3e229e74913e6db3",
10
+ "type": "github"
11
+ },
12
+ "original": {
13
+ "owner": "NixOS",
14
+ "ref": "nixos-unstable",
15
+ "repo": "nixpkgs",
16
+ "type": "github"
17
+ }
18
+ },
19
+ "root": {
20
+ "inputs": {
21
+ "nixpkgs": "nixpkgs"
22
+ }
23
+ }
24
+ },
25
+ "root": "root",
26
+ "version": 7
27
+ }
data/flake.nix ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ description = "MailerLite Go SDK environment";
3
+
4
+ inputs = {
5
+ nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
6
+ };
7
+
8
+ outputs = { self, nixpkgs }:
9
+ let
10
+ systems = [ "aarch64-darwin" "x86_64-darwin" "aarch64-linux" "x86_64-linux" ];
11
+ forEachSystem = f:
12
+ nixpkgs.lib.genAttrs systems (system: f system nixpkgs.legacyPackages.${system});
13
+ in
14
+ {
15
+ devShells = forEachSystem (system: pkgs: {
16
+ default = pkgs.mkShell {
17
+ packages = with pkgs; [
18
+ lefthook
19
+ gitleaks
20
+ git-secrets
21
+ ];
22
+ };
23
+ });
24
+ };
25
+ }
data/lefthook.yml ADDED
@@ -0,0 +1,14 @@
1
+ ---
2
+ pre-commit:
3
+ commands:
4
+ gitleaks:
5
+ run: gitleaks git --pre-commit --redact --staged --verbose --no-banner
6
+ fail_text: Staged changes contain a secret. Remove it; never commit and revert.
7
+ secrets:
8
+ run: |
9
+ git secrets --list | grep -q 'mssp' || {
10
+ git secrets --register-aws
11
+ git secrets --add 'mlsn\.[A-Za-z0-9._-]+'
12
+ git secrets --add 'mssp\.[A-Za-z0-9._-]+'
13
+ }
14
+ git secrets --scan {staged_files}