Added recipe endpoint

Necessary to perform API requests and acquire models for further processing. This also wires up the client and exposes the endpoint for immediate use.

Milestone: minor
This commit is contained in:
Brooke Kuhlmann
2026-01-15 09:18:34 -07:00
parent d0d515a948
commit 7ea4e06caa
5 changed files with 160 additions and 0 deletions
+4
View File
@@ -17,6 +17,7 @@ module TRMNL
endpoint_log: :log,
endpoint_models: :models,
endpoint_palettes: :palettes,
endpoint_recipes: :recipes,
endpoint_setup: :setup
]
@@ -29,6 +30,7 @@ module TRMNL
endpoint_log: :type,
endpoint_models: :type,
endpoint_palettes: :type,
endpoint_recipes: :type,
endpoint_setup: :type
]
@@ -53,6 +55,8 @@ module TRMNL
def palettes = endpoint_palettes.call
def recipes(**) = endpoint_recipes.call(**)
def setup(**) = endpoint_setup.call(**)
end
end
+1
View File
@@ -17,6 +17,7 @@ module TRMNL
register(:log) { Log.new }
register(:models) { Model.new }
register(:palettes) { Palette.new }
register(:recipes) { Recipe.new }
register(:setup) { Setup.new }
end
end
+31
View File
@@ -0,0 +1,31 @@
# frozen_string_literal: true
require "inspectable"
require "pipeable"
module TRMNL
module API
module Endpoints
# Handles API request/response.
class Recipe
include TRMNL::API::Dependencies[
:requester,
contract: "contracts.recipe",
model: "models.recipe"
]
include Inspectable[contract: :type]
include Pipeable
def call(**)
pipe(
requester.get("recipes.json", **),
try(:parse, catch: JSON::ParserError),
validate(contract, as: :to_h),
to(model, :for)
)
end
end
end
end
end
+10
View File
@@ -103,6 +103,15 @@ RSpec.describe TRMNL::API::Client do
end
end
describe "#recipe" do
let(:endpoint) { instance_spy TRMNL::API::Endpoints::Recipe }
it "messages endpoint" do
client = described_class.new endpoint_recipes: endpoint
expect(client.recipes).to have_received(:call).with(no_args)
end
end
describe "#setup" do
let(:endpoint) { instance_spy TRMNL::API::Endpoints::Setup }
@@ -122,6 +131,7 @@ RSpec.describe TRMNL::API::Client do
endpoint_log: "TRMNL::API::Endpoints::Log",
endpoint_models: "TRMNL::API::Endpoints::Model",
endpoint_palettes: "TRMNL::API::Endpoints::Palette",
endpoint_recipes: "TRMNL::API::Endpoints::Recipe",
endpoint_setup: "TRMNL::API::Endpoints::Setup"
)
end
+114
View File
@@ -0,0 +1,114 @@
# frozen_string_literal: true
require "spec_helper"
RSpec.describe TRMNL::API::Endpoints::Recipe do
subject(:endpoint) { described_class.new requester: }
include_context "with application dependencies"
let :requester do
TRMNL::API::Requester.new http: do |settings|
settings.uri = "https://usetrmnl.com"
end
end
describe "#call" do
context "with success" do
let :http do
HTTP::Fake::Client.new do
get "/recipes.json" do
headers["Content-Type"] = "application/json"
status 200
<<~JSON
{
"data": [
{
"id": 1,
"name": "test",
"screenshot_url": "https://screens.trmnl.io/test.png",
"published_at": "2026-01-02T03:04:05.000Z",
"custom_fields": [],
"author_bio": {
"keyname": "test",
"name": "Test",
"field_type": "author_bio"
},
"stats": {
"installs": 1,
"forks": 2
}
}
],
"total": 2,
"from": 1,
"to": 2,
"per_page": 25,
"current_page": 1,
"prev_page_url": null,
"next_page_url": "/recipes.json?page=2"
}
JSON
end
end
end
it "answers record" do
expect(endpoint.call.success).to match(
TRMNL::API::Models::Recipe[
data: [
TRMNL::API::Models::Recipes::Entry[
id: 1,
name: "test",
published_at: Time.parse("2026-01-02T03:04:05.000Z"),
screenshot_url: "https://screens.trmnl.io/test.png",
author: TRMNL::API::Models::Recipes::Author[
keyname: "test",
name: "Test",
field_type: "author_bio",
description_locales: {}
],
custom_fields: [],
statistics: TRMNL::API::Models::Recipes::Statistics[installs: 1, forks: 2]
]
],
total: 2,
from: 1,
to: 2,
per_page: 25,
current_page: 1,
prev_page_url: nil,
next_page_url: "/recipes.json?page=2"
]
)
end
end
context "with failure" do
let :http do
HTTP::Fake::Client.new do
get "/recipes.json" do
headers["Content-Type"] = "application/json"
status 404
<<~JSON
{"error": "Danger!"}
JSON
end
end
end
it "answers failure" do
result = described_class.new(requester:).call
expect(result).to match(Failure(be_a(HTTP::Response)))
end
end
end
describe "#inspect" do
it "has inspected attributes" do
expect(described_class.new.inspect).to match_inspection(contract: "Dry::Schema::JSON")
end
end
end