Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
9b3179f
migration: handle data handling in formatter by removing attribute ac…
saint-james-fr Apr 9, 2025
ead6660
migration: replace id by document_id in validations and builder
saint-james-fr Apr 9, 2025
4ef268f
migration: update README to replace id with document_id in examples
saint-james-fr Apr 9, 2025
c7251bc
migration: update integration tests to replace id with document_id an…
saint-james-fr Apr 9, 2025
b342fdb
missing example in README
saint-james-fr Apr 9, 2025
6b09619
docs: V5 disclaimer
saint-james-fr Apr 9, 2025
6a733f1
fix: clause guard to_html
saint-james-fr Apr 9, 2025
b6788cb
migration: update client and formatter tests
saint-james-fr Apr 9, 2025
8df074e
fix: add OpenStruct requirement to strapi_ruby.rb
saint-james-fr Apr 9, 2025
503df65
fix: update response status handling in StrapiRuby client
saint-james-fr Apr 9, 2025
c23a688
migration: update integration tests to use documentId instead of id f…
saint-james-fr Apr 9, 2025
71320c2
fix: add nil checks for response body and answer data in StrapiRuby c…
saint-james-fr Apr 9, 2025
87ef656
fix: add nil check for answer before processing data and meta in Stra…
saint-james-fr Apr 9, 2025
ec58c2e
fix: add nil value check in StrapiRuby parameters to prevent CGI.esca…
saint-james-fr Apr 9, 2025
0b373ce
refactor: remove debug print statement from StrapiRuby configuration …
saint-james-fr Apr 9, 2025
d080f4a
refactor: skip nil values in StrapiRuby parameters to improve CGI.esc…
saint-james-fr Apr 9, 2025
98c6b99
refactor: use FlatParamsEncoder in StrapiRuby client to prevent doubl…
saint-james-fr Apr 9, 2025
9bfb2b5
migrate integration tests
saint-james-fr Apr 9, 2025
ff91fca
docs: fix README
saint-james-fr Apr 9, 2025
fcb6ef3
chore: bump version to 1.0.0 in StrapiRuby module
saint-james-fr Apr 10, 2025
31a01ae
docs: update Gemfile entry for strapi_ruby to specify version constra…
saint-james-fr Apr 10, 2025
9994ecb
fix: updated gemfile.lock
saint-james-fr Apr 10, 2025
81aa976
fix: update gemfile.lock
saint-james-fr Apr 10, 2025
3d8e569
docs: update Gemfile entry for strapi_ruby to specify version constra…
saint-james-fr Apr 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
strapi_ruby (0.1.4)
strapi_ruby (1.0.1)
colorize (~> 1.1.0)
faraday (~> 2.7)
redcarpet (~> 3.6)
Expand Down
36 changes: 23 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@

I think it's one of the actual coolest solution for integrating a CMS into Rails for example, so let's dive in!

## Important Notice: Strapi V5 and strapi_ruby

Starting from version >=1.0.0, the StrapiRuby gem is only compatible with Strapi version 5 and above. This update includes significant changes to align with the new response format introduced in Strapi v5. Key changes include:

- **Flattened Response Format**: The `attributes` object has been removed, and fields are now directly part of the `data` object.
- **ID Handling**: The `id` field has been replaced with `documentId` to uniquely identify resources.
- **Filter Adjustments**: Filters that previously used `id` should now use `documentId`.

These changes ensure that the StrapiRuby gem takes full advantage of the improvements in Strapi v5, providing a more streamlined and efficient API interaction experience. Please ensure your Strapi server is updated to version 5 or later to use this version of the gem.

Following will be the documentation for 1.xx release.

## Table of contents

- [Installation](#installation)
Expand Down Expand Up @@ -43,15 +55,13 @@ I think it's one of the actual coolest solution for integrating a CMS into Rails

## Installation

Add this line to your application's Gemfile:
Add this line to your application's Gemfile - **make sure to take at least 1.0.1 version**

```ruby
# Gemfile
gem "strapi_ruby"
gem 'strapi_ruby', '~> 1.0.1'
```



Then if you use Rails, run in your terminal to generate a config initializer. Otherwise copy paste and fill the config block.

```bash
Expand Down Expand Up @@ -105,9 +115,9 @@ data = answer.data
meta = answer.meta

# Access a specific attribute
answer = StrapiRuby.get(resource: :articles, id: 2)
answer = StrapiRuby.get(resource: :articles, document_id: "clkgylmcc000008lcdd868feh")
article = answer.data
title = article.attributes.title
title = article.title

# If an error occur, it will be raised to be rescued and displayed in the answer.
data = answer.data # => nil
Expand All @@ -125,7 +135,7 @@ answer = StrapiRuby.get(resource: :restaurants)


# Get a specific element
StrapiRuby.get(resource: :restaurants, id: 1)
StrapiRuby.get(resource: :restaurants, document_id: "clkgylmcc000008lcdd868feh")
```

#### .post
Expand All @@ -143,15 +153,15 @@ StrapiRuby.post(resource: :articles,
```ruby
# Update a specific item, return item updated
StrapiRuby.put(resource: :articles,
id: 23,
document_id: "clkgylmcc000008lcdd868feh",
data: {content: "'I've edited this article via a PUT request'"})
```

#### .delete

```ruby
# Delete an item, return item deleted
StrapiRuby.delete(resource: :articles, id: 12)
StrapiRuby.delete(resource: :articles, document_id: "clkgylmcc000008lcdd868feh")

```

Expand All @@ -176,7 +186,7 @@ end
<ul>
<% @articles.data.each do |article| %>
<li>
<%= article.attributes.title %>
<%= article.title %>
</li>
<% end %>
</ul>
Expand Down Expand Up @@ -324,11 +334,11 @@ StrapiRuby.get(resource: :users, filters: { username: { "$eq" => "John" } })
# Using $in operator to match multiples values
StrapiRuby.get(resource: :restaurants,
filters: {
id: {
"$in" => ["3", "6", "8"],
documentId: {
"$in" => ["clkgylmcc000008lcdd868feh", "clkgylw7d000108lc4rw1bb6s"],
},
})
# => /restaurants?filters[id][$in][0]=3&filters[id][$in][1]=6&filters[id][$in][2]=8
# => /restaurants?filters[documentId][$in][0]=clkgylmcc000008lcdd868feh&filters[documentId][$in][0]=clkgylw7d000108lc4rw1bb6s

# --------------------------------

Expand Down
1 change: 1 addition & 0 deletions lib/strapi_ruby.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# frozen_string_literal: true
require "ostruct"

require_relative "strapi_ruby/version"
require_relative "strapi_ruby/validations"
Expand Down
7 changes: 4 additions & 3 deletions lib/strapi_ruby/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ def set_connection(options, &block)
"User-Agent" => "StrapiRuby/#{StrapiRuby::VERSION}" }

Faraday.new(url: url) do |faraday|
faraday.request :url_encoded
# Use FlatParamsEncoder to prevent double encoding of special characters
faraday.options.params_encoder = Faraday::FlatParamsEncoder
faraday.adapter Faraday.default_adapter
block&.call(faraday)
faraday.headers = default_headers.merge(faraday.headers)
Expand Down Expand Up @@ -69,9 +70,9 @@ def build_data_payload(body)

# rubocop:disable Metrics/AbcSize
def handle_response(response)
body = convert_json_to_open_struct(response.body)
body = convert_json_to_open_struct(response.body) unless response.body.empty?
case response.status
when 200
when 200, 201
body
when 400
raise BadRequestError.new(body.error.message, response.status)
Expand Down
6 changes: 3 additions & 3 deletions lib/strapi_ruby/endpoint/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Endpoint
class Builder
def initialize(options = {})
@resource = options[:resource]
@id = options[:id]
@document_id = options[:document_id]
@query = Query.new(options).call
@result = nil
end
Expand All @@ -20,7 +20,7 @@ def build_endpoint
@result = if collection?
"#{base_uri}/#{@resource}"
else
"#{base_uri}/#{@resource}/#{@id}"
"#{base_uri}/#{@resource}/#{@document_id}"
end
end

Expand All @@ -29,7 +29,7 @@ def append_query
end

def collection?
@id.nil?
@document_id.nil?
end

def base_uri
Expand Down
5 changes: 2 additions & 3 deletions lib/strapi_ruby/endpoint/strapi_parameters.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,11 @@ def traverse_hash(hash, parent_key = nil)
traverse_hash({ index => item }, current_key)
end
else
# We can pass values as symbols but we need to convert them to string
# to be able to escape them
value = value.to_s if value.is_a?(Symbol)
next if value.nil? # Skip nil values
"#{current_key}=#{CGI.escape(value)}"
end
end.join("&")
end.compact.join("&")
end
end
end
Expand Down
8 changes: 4 additions & 4 deletions lib/strapi_ruby/formatter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ def convert_to_datetime!(data)
return unless StrapiRuby.config.convert_to_datetime

if collection?(data)
data.each { |item| parse_into_datetime!(item.attributes) }
data.each { |item| parse_into_datetime!(item) }
else
parse_into_datetime!(data.attributes)
parse_into_datetime!(data)
end
end

Expand Down Expand Up @@ -62,9 +62,9 @@ def collection?(data)

def convert_to_html!(data)
if collection?(data)
data.each { |item| convert_attributes!(item.attributes) }
data.each { |item| convert_attributes!(item) }
else
convert_attributes!(data.attributes)
convert_attributes!(data)
end
end

Expand Down
6 changes: 4 additions & 2 deletions lib/strapi_ruby/interface.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ def request(http_verb, options = {})
validate_options(options)
@endpoint = build_endpoint(options)
answer = build_answer(http_verb, @endpoint, options)
data = format_data(answer.data, options)
meta = answer.meta
if answer
data = format_data(answer.data, options) unless answer.data.nil?
meta = answer.meta unless answer.meta.nil?
end

return_success_open_struct(data, meta, options)
rescue StrapiRuby::ClientError, StrapiRuby::ConfigurationError => e
Expand Down
2 changes: 2 additions & 0 deletions lib/strapi_ruby/markdown.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ class Markdown
include Singleton

def to_html(markdown)
return "" if markdown.nil?

markdown_renderer.render(markdown)
end

Expand Down
6 changes: 3 additions & 3 deletions lib/strapi_ruby/validations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def validate_config(config)
def validate_options(options)
validate_config_presence
validate_resource(options)
validate_id(options)
validate_document_id(options)
validate_show_endpoint_params(options)
validate_body(options)
end
Expand Down Expand Up @@ -47,8 +47,8 @@ def validate_resource(options)
raise TypeError, "#{ErrorMessage.expected_string_symbol} Got #{options[:resource].class.name}" unless options[:resource].is_a?(String) || options[:resource].is_a?(Symbol)
end

def validate_id(options)
raise TypeError, "#{ErrorMessage.expected_integer} Got #{options[:id].class.name}" if options.key?(:id) && !options[:id].is_a?(Integer)
def validate_document_id(options)
raise TypeError, "#{ErrorMessage.expected_string} Got #{options[:document_id].class.name}" if options.key?(:document_id) && !options[:document_id].is_a?(String)
end

def validate_show_endpoint_params(options)
Expand Down
2 changes: 1 addition & 1 deletion lib/strapi_ruby/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module StrapiRuby
VERSION = "0.1.4"
VERSION = "1.0.1"
end
48 changes: 24 additions & 24 deletions spec/client/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@
let(:client) { StrapiRuby::Client.new(strapi_server_uri: "https://www.example.com") }

before do
stub_request(:get, "https://www.example.com/articles/1").to_return(status: 200, body: '{"data": {"attributes":{"id": 1, "title": "Example"}}}')
stub_request(:post, "https://www.example.com/articles").to_return(status: 200, body: '{"data": {"attributes": {"title": "This is a new example"}}}')
stub_request(:put, "https://www.example.com/articles/1").to_return(status: 200, body: '{"data": {"attributes": {"title": "This is a modified example"}}}')
stub_request(:delete, "https://www.example.com/articles/1").to_return(status: 200, body: '{"data": {"attributes": {"title": "This is a deleted example"}}}')
stub_request(:get, "https://www.example.com/articles/clkgylw7d000108lc4rw1bb6s").to_return(status: 200, body: '{"data" :{"documentId": "clkgylw7d000108lc4rw1bb6s", "title": "Example"}}')
stub_request(:post, "https://www.example.com/articles").to_return(status: 200, body: '{"data": {"documentId": "clkgylw7d000108lc4rw1bb6s", "title": "This is a new example"}}')
stub_request(:put, "https://www.example.com/articles/clkgylw7d000108lc4rw1bb6s").to_return(status: 200, body: '{"data": {"documentId": "clkgylw7d000108lc4rw1bb6s", "title": "This is a modified example"}}')
stub_request(:delete, "https://www.example.com/articles/clkgylw7d000108lc4rw1bb6s").to_return(status: 200, body: '{"data": {"documentId": "clkgylw7d000108lc4rw1bb6s", "title": "This is a deleted example"}}')
end

describe "#get" do
it "sends a GET request to the specified endpoint" do
faraday_response = client.connection.get("/articles/1")
faraday_response = client.connection.get("/articles/clkgylw7d000108lc4rw1bb6s")
expect(faraday_response.status).to eq(200)
end

it "returns an OpenStruct object" do
response = client.get("/articles/1")
response = client.get("/articles/clkgylw7d000108lc4rw1bb6s")
expect(response.class).to eq(OpenStruct)
end

Expand All @@ -41,7 +41,7 @@
expect(faraday_response.status).to eq(200)
response = client.post("/articles", { title: "This is a new example" })
p
expect(response.data.attributes.title).to eq("This is a new example")
expect(response.data.title).to eq("This is a new example")
end

it "returns an OpenStruct object" do
Expand All @@ -65,57 +65,57 @@

describe "#put" do
it "sends a PUT request to the specified endpoint" do
faraday_response = client.connection.put("/articles/1", { title: "This is a modified example" })
faraday_response = client.connection.put("/articles/clkgylw7d000108lc4rw1bb6s", { title: "This is a modified example" })
expect(faraday_response.status).to eq(200)

response = client.put("/articles/1", { title: "This is a modified example" })
expect(response.data.attributes.title).to eq("This is a modified example")
response = client.put("/articles/clkgylw7d000108lc4rw1bb6s", { title: "This is a modified example" })
expect(response.data.title).to eq("This is a modified example")
end

it "returns an OpenStruct object" do
response = client.put("/articles/1", { title: "This is a modified example" })
response = client.put("/articles/clkgylw7d000108lc4rw1bb6s", { title: "This is a modified example" })
expect(response.class).to eq(OpenStruct)
end

it "handles connection errors gracefully" do
allow_any_instance_of(Faraday::Connection).to receive(:put).and_raise(Faraday::ConnectionFailed.new("Connection failed"))
expect { client.put("/resource/1", {}) }.to raise_error(StrapiRuby::ConnectionError)
expect { client.put("/resource/clkgylw7d000108lc4rw1bb6s", {}) }.to raise_error(StrapiRuby::ConnectionError)
end
it "handles timeout errors gracefully" do
allow_any_instance_of(Faraday::Connection).to receive(:put).and_raise(Faraday::TimeoutError.new("Timeout Error"))
expect { client.put("/resource/1", {}) }.to raise_error(StrapiRuby::ConnectionError)
expect { client.put("/resource/clkgylw7d000108lc4rw1bb6s", {}) }.to raise_error(StrapiRuby::ConnectionError)
end
it "handles other errors gracefully" do
allow_any_instance_of(Faraday::Connection).to receive(:put).and_raise(StandardError.new("Something bad happened"))
expect { client.put("/resource/1", {}) }.to raise_error(StrapiRuby::ConnectionError)
expect { client.put("/resource/clkgylw7d000108lc4rw1bb6s", {}) }.to raise_error(StrapiRuby::ConnectionError)
end
end

describe "#delete" do
it "sends a DELETE request to the specified endpoint" do
faraday_response = client.connection.delete("/articles/1")
faraday_response = client.connection.delete("/articles/clkgylw7d000108lc4rw1bb6s")
expect(faraday_response.status).to eq(200)

response = client.delete("/articles/1")
expect(response.data.attributes.title).to eq("This is a deleted example")
response = client.delete("/articles/clkgylw7d000108lc4rw1bb6s")
expect(response.data.title).to eq("This is a deleted example")
end

it "returns an OpenStruct object" do
response = client.put("/articles/1", { title: "This is a deleted example" })
response = client.put("/articles/clkgylw7d000108lc4rw1bb6s", { title: "This is a deleted example" })
expect(response.class).to eq(OpenStruct)
end

it "handles connection errors gracefully" do
allow_any_instance_of(Faraday::Connection).to receive(:delete).and_raise(Faraday::ConnectionFailed.new("Connection failed"))
expect { client.delete("/resource/1") }.to raise_error(StrapiRuby::ConnectionError)
expect { client.delete("/resource/clkgylw7d000108lc4rw1bb6s") }.to raise_error(StrapiRuby::ConnectionError)
end
it "handles timeout errors gracefully" do
allow_any_instance_of(Faraday::Connection).to receive(:delete).and_raise(Faraday::TimeoutError.new("Timeout Error"))
expect { client.delete("/resource/1") }.to raise_error(StrapiRuby::ConnectionError)
expect { client.delete("/resource/clkgylw7d000108lc4rw1bb6s") }.to raise_error(StrapiRuby::ConnectionError)
end
it "handles other errors gracefully" do
allow_any_instance_of(Faraday::Connection).to receive(:delete).and_raise(StandardError.new("Something bad happened"))
expect { client.delete("/resource/1") }.to raise_error(StrapiRuby::ConnectionError)
expect { client.delete("/resource/clkgylw7d000108lc4rw1bb6s") }.to raise_error(StrapiRuby::ConnectionError)
end
end

Expand Down Expand Up @@ -146,15 +146,15 @@

describe "#handle_response" do
context "when the response status is 200" do
let(:response) { double(status: 200, message: "OK", body: '{"data": {"id": 1, "name": "Example"}}') }
let(:response) { double(status: 200, message: "OK", body: '{"data": {"documentId": "clkgylw7d000108lc4rw1bb6s", "title": "Example"}}') }

it "returns an OpenStruct object" do
expect(client.send(:handle_response, response)).to be_a(OpenStruct)
end

it "parses the JSON data into the OpenStruct object" do
expect(client.send(:handle_response, response).data.id).to eq(1)
expect(client.send(:handle_response, response).data.name).to eq("Example")
expect(client.send(:handle_response, response).data.documentId).to eq("clkgylw7d000108lc4rw1bb6s")
expect(client.send(:handle_response, response).data.title).to eq("Example")
end
end

Expand Down
1 change: 0 additions & 1 deletion spec/config/config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
config.strapi_server_uri = "https://example.com"
config.strapi_token = "124"
config.faraday = faraday_string
p config
expect { config.call }.to raise_error(TypeError)
end
end
Expand Down
Loading