medium_to_webflow 0.1.0 → 0.2.0.beta1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d3abc7bf7338f6c7c0805da0e39734887dec08d43278091001bc3658b48b5186
4
- data.tar.gz: e2f5a6ce102f3aed88ff2715ea616ffc9c54d4f980c2e5dd2de19fad6726ee99
3
+ metadata.gz: 064aff3e131593838b26d0592e549d504afaa66cc7b053b35e5befedf7d60f73
4
+ data.tar.gz: e0b5c19118e1746b5f68b8b5a3100c656bca29f6023e13948d57a856b11d2662
5
5
  SHA512:
6
- metadata.gz: b7694736f114a369626a92f03d6251f89eea3661fb320f57ea63ca565e13c67de913c37ba2df02fa8f3e0eb927e3630b4da3d48b8cff9916b9dd8af9956f5f23
7
- data.tar.gz: '087aa51ca02af37dbaa4b2c39d89cd4cc13ba6cf420022e8259fabdfeea4063455b9f5419c7157d7f6a34a102e133c014c36540f92f5bf9cb2f315e5913e3a86'
6
+ metadata.gz: 0263e5d23b9da36898d14fcd246c8eb85262997d2c623ac72de52a33a59730c602451eaecd7e195b8820ca2081624ee0ee4c4ef59ea7c36db9774687bd3a0ec8
7
+ data.tar.gz: ca5ab0e0eda799500c9b151a4818c6768898ef92ace248dfe4d00f776b80dd47277d8597208bc3f10514af59c4d1074482a47f7bd9f4873a9f1e9fbb6dbf57b5
@@ -8,8 +8,7 @@ module MediumToWebflow
8
8
 
9
9
  def initialize(medium_username:, webflow_api_token:, webflow_collection_id:, field_mappings:)
10
10
  @medium_username = medium_username
11
- @webflow_api_token = webflow_api_token
12
- @webflow_collection_id = webflow_collection_id
11
+ @webflow_adapter = Webflow::Adapter.new(webflow_api_token, webflow_collection_id)
13
12
  @field_mappings = field_mappings
14
13
  @logger = MediumToWebflow.configuration.logger
15
14
  end
@@ -21,7 +20,7 @@ module MediumToWebflow
21
20
  medium_posts = fetch_medium_posts
22
21
  @logger.info "Found #{medium_posts.count} posts to sync"
23
22
 
24
- sync_to_webflow(medium_posts)
23
+ sync_medium_posts_to_webflow(medium_posts)
25
24
 
26
25
  @logger.info "Sync completed successfully!"
27
26
  rescue StandardError => e
@@ -36,18 +35,61 @@ module MediumToWebflow
36
35
  Medium::Client.new(username: @medium_username).fetch_posts
37
36
  end
38
37
 
39
- def sync_to_webflow(posts)
40
- webflow_client = Webflow::Client.new(
41
- api_token: @webflow_api_token,
42
- collection_id: @webflow_collection_id,
43
- field_mappings: @field_mappings
44
- )
38
+ def sync_medium_posts_to_webflow(medium_posts)
39
+ medium_posts.each_with_index do |medium_post, index|
40
+ @logger.debug "Processing post: #{medium_post.title}"
41
+ sync_medium_post_to_webflow(medium_post)
42
+ @logger.info "Successfully synced: #{medium_post.title} (#{index + 1}/#{medium_posts.count})"
43
+ end
44
+ end
45
+
46
+ def sync_medium_post_to_webflow(medium_post)
47
+ fields = build_webflow_fields(medium_post)
48
+ medium_slug_field_name = @field_mappings.key("slug")
49
+ existing_item = find_webflow_item_by_slug(medium_post.send(medium_slug_field_name))
50
+
51
+ if existing_item
52
+ handle_existing_webflow_item(existing_item, fields, medium_post)
53
+ else
54
+ create_webflow_item(fields)
55
+ end
56
+ end
57
+
58
+ def find_webflow_item_by_slug(slug)
59
+ @webflow_adapter.find_by_slug(slug)
60
+ end
61
+
62
+ def handle_existing_webflow_item(existing_item, fields, medium_post)
63
+ if MediumToWebflow.configuration.force_update
64
+ @logger.debug "Forcing update of existing item: #{existing_item[:id]}"
65
+ @webflow_adapter.update_item(existing_item[:id], fields)
66
+ else
67
+ @logger.info "Skipping existing item: #{medium_post.title} (use --force-update to override)"
68
+ end
69
+ end
70
+
71
+ def create_webflow_item(fields)
72
+ @webflow_adapter.create_item(fields)
73
+ end
74
+
75
+ def build_webflow_fields(medium_post)
76
+ @field_mappings.each_with_object({}) do |(medium_field_name, webflow_field_name), fields|
77
+ value = medium_post.public_send(medium_field_name)
78
+ next if value.nil?
45
79
 
46
- posts.each_with_index do |post, index|
47
- @logger.debug "Processing post: #{post.title}"
48
- webflow_client.upsert_post(post)
49
- @logger.info "Successfully synced: #{post.title} (#{index + 1}/#{posts.count})"
80
+ fields[webflow_field_name] = process_field_value(medium_field_name, value)
50
81
  end
51
82
  end
83
+
84
+ def process_field_value(medium_field_name, value)
85
+ # Handle the image field by converting it to Webflow's expected format { url: "image_url" }
86
+ return { url: value } if medium_field_name == :image_url
87
+
88
+ # Convert DateTime/Time objects to ISO8601 format for Webflow's date fields
89
+ return value.iso8601 if value.respond_to?(:iso8601)
90
+
91
+ # Return value as-is for all other field types
92
+ value
93
+ end
52
94
  end
53
95
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MediumToWebflow
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0.beta1"
5
5
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webflow"
4
+
5
+ module MediumToWebflow
6
+ module Webflow
7
+ class Adapter
8
+ def initialize(api_token, collection_id)
9
+ @client = ::Webflow::Client.new(api_token)
10
+ @collection_id = collection_id
11
+ @logger = MediumToWebflow.configuration.logger
12
+ end
13
+
14
+ def find_by_slug(slug)
15
+ @client.list_items(@collection_id, query_params: { slug: slug }).first
16
+ rescue ::Webflow::Error => e
17
+ handle_error("list_items", e)
18
+ nil
19
+ end
20
+
21
+ def create_item(fields)
22
+ @logger.debug "Creating Webflow item in collection: #{@collection_id}"
23
+ @logger.debug "Fields: #{fields.inspect}" if MediumToWebflow.configuration.verbose
24
+
25
+ @client.create_item(@collection_id, fields, is_draft: true)
26
+ rescue ::Webflow::Error => e
27
+ handle_error("create_item", e)
28
+ end
29
+
30
+ def update_item(item_id, fields)
31
+ @logger.debug "Updating Webflow item: #{item_id} in collection: #{@collection_id}"
32
+ @logger.debug "Fields: #{fields.inspect}" if MediumToWebflow.configuration.verbose
33
+
34
+ @client.update_item(@collection_id, item_id, fields, is_draft: true)
35
+ rescue ::Webflow::Error => e
36
+ handle_error("update_item", e)
37
+ end
38
+
39
+ private
40
+
41
+ def handle_error(operation, error)
42
+ error_message = "Webflow #{operation} operation failed: #{error.message}"
43
+ @logger.error error_message
44
+ raise MediumToWebflow::Error, error_message
45
+ end
46
+ end
47
+ end
48
+ end
@@ -9,7 +9,7 @@ require_relative "medium_to_webflow/errors"
9
9
 
10
10
  require_relative "medium_to_webflow/medium/client"
11
11
  require_relative "medium_to_webflow/medium/post"
12
- require_relative "medium_to_webflow/webflow/client"
12
+ require_relative "medium_to_webflow/webflow/adapter"
13
13
  require_relative "medium_to_webflow/sync_service"
14
14
  require_relative "medium_to_webflow/cli"
15
15
 
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: medium_to_webflow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paulo Santos
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-12-13 00:00:00.000000000 Z
10
+ date: 2025-03-18 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: httparty
@@ -66,6 +65,20 @@ dependencies:
66
65
  - - "~>"
67
66
  - !ruby/object:Gem::Version
68
67
  version: '1.3'
68
+ - !ruby/object:Gem::Dependency
69
+ name: webflow-rb
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '1.1'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '1.1'
69
82
  description: A library and CLI tool to fetch posts from Medium and sync them to Webflow
70
83
  CMS collections
71
84
  email:
@@ -88,7 +101,7 @@ files:
88
101
  - lib/medium_to_webflow/medium/post.rb
89
102
  - lib/medium_to_webflow/sync_service.rb
90
103
  - lib/medium_to_webflow/version.rb
91
- - lib/medium_to_webflow/webflow/client.rb
104
+ - lib/medium_to_webflow/webflow/adapter.rb
92
105
  homepage: https://github.com/deemaze/medium_to_webflow
93
106
  licenses:
94
107
  - MIT
@@ -98,7 +111,6 @@ metadata:
98
111
  source_code_uri: https://github.com/deemaze/medium_to_webflow
99
112
  changelog_uri: https://github.com/deemaze/medium_to_webflow/blob/main/CHANGELOG.md
100
113
  rubygems_mfa_required: 'true'
101
- post_install_message:
102
114
  rdoc_options: []
103
115
  require_paths:
104
116
  - lib
@@ -113,8 +125,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
113
125
  - !ruby/object:Gem::Version
114
126
  version: '0'
115
127
  requirements: []
116
- rubygems_version: 3.5.22
117
- signing_key:
128
+ rubygems_version: 3.6.2
118
129
  specification_version: 4
119
130
  summary: Sync Medium posts to Webflow CMS collections
120
131
  test_files: []
@@ -1,99 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module MediumToWebflow
4
- module Webflow
5
- class Client
6
- include HTTParty
7
- base_uri "https://api.webflow.com/v2"
8
- headers "Accept" => "application/json"
9
- headers "Content-Type" => "application/json"
10
-
11
- def initialize(api_token:, collection_id:, field_mappings:)
12
- @api_token = api_token
13
- @collection_id = collection_id
14
- @field_mappings = field_mappings
15
- self.class.headers "Authorization" => "Bearer #{api_token}"
16
- @logger = MediumToWebflow.configuration.logger
17
- end
18
-
19
- def upsert_post(medium_post)
20
- fields = build_fields(medium_post)
21
- medium_slug_field = @field_mappings.key("slug")
22
- existing_item = find_item(slug: medium_post.send(medium_slug_field))
23
-
24
- handle_existing_or_create_item(existing_item, fields, medium_post)
25
- end
26
-
27
- private
28
-
29
- def handle_existing_or_create_item(existing_item, fields, medium_post)
30
- if existing_item
31
- handle_existing_item(existing_item, fields, medium_post)
32
- else
33
- create_item(fields: fields)
34
- end
35
- end
36
-
37
- def handle_existing_item(existing_item, fields, medium_post)
38
- if MediumToWebflow.configuration.force_update
39
- @logger.debug "Forcing update of existing item: #{existing_item["id"]}"
40
- update_item(item_id: existing_item["id"], fields: fields)
41
- else
42
- @logger.info "Skipping existing item: #{medium_post.title} (use --force-update to override)"
43
- end
44
- end
45
-
46
- def find_item(slug:)
47
- response = self.class.get("/collections/#{@collection_id}/items/live", query: { slug: slug })
48
-
49
- handle_response(response)["items"]&.first
50
- end
51
-
52
- def create_item(fields:)
53
- @logger.debug "Creating Webflow item in collection: #{@collection_id}"
54
- @logger.debug "Fields: #{fields.inspect}" if MediumToWebflow.configuration.verbose
55
-
56
- response = self.class.post("/collections/#{@collection_id}/items/live", body: {
57
- fieldData: fields
58
- }.to_json)
59
- handle_response(response)
60
- end
61
-
62
- def update_item(item_id:, fields:)
63
- @logger.debug "Updating Webflow item: #{item_id} in collection: #{@collection_id}"
64
- @logger.debug "Fields: #{fields.inspect}" if MediumToWebflow.configuration.verbose
65
-
66
- response = self.class.patch("/collections/#{@collection_id}/items/#{item_id}/live", body: {
67
- fieldData: fields
68
- }.to_json)
69
- handle_response(response)
70
- end
71
-
72
- def build_fields(medium_post)
73
- @field_mappings.each_with_object({}) do |(medium_field, webflow_field), fields|
74
- value = medium_post.public_send(medium_field)
75
- next if value.nil?
76
-
77
- fields[webflow_field] = process_field_value(medium_field, value)
78
- end
79
- end
80
-
81
- def process_field_value(field, value)
82
- # Handle the image field by converting it to Webflow's expected format { url: "image_url" }
83
- return { url: value } if field == :image_url
84
-
85
- # Convert DateTime/Time objects to ISO8601 format for Webflow's date fields
86
- return value.iso8601 if value.respond_to?(:iso8601)
87
-
88
- # Return value as-is for all other field types
89
- value
90
- end
91
-
92
- def handle_response(response)
93
- return response.parsed_response if response.success?
94
-
95
- raise Error, "Webflow API error: #{response.code} - #{response.body}"
96
- end
97
- end
98
- end
99
- end