From 388509d5dd2f84393317576e1b83bb78675dd078 Mon Sep 17 00:00:00 2001 From: Brooke Kuhlmann Date: Fri, 18 Jul 2025 13:18:32 -0600 Subject: [PATCH] Added model endpoint Necessary to perform API request, parse the response, validate the response, and hydrate into a whole value object for further processing. Milestone: minor --- lib/trmnl/api/endpoints/container.rb | 1 + lib/trmnl/api/endpoints/model.rb | 32 ++++++++ spec/lib/trmnl/api/endpoints/model_spec.rb | 93 ++++++++++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 lib/trmnl/api/endpoints/model.rb create mode 100644 spec/lib/trmnl/api/endpoints/model_spec.rb diff --git a/lib/trmnl/api/endpoints/container.rb b/lib/trmnl/api/endpoints/container.rb index bd100bf..4f1b6b0 100644 --- a/lib/trmnl/api/endpoints/container.rb +++ b/lib/trmnl/api/endpoints/container.rb @@ -13,6 +13,7 @@ module TRMNL register(:display) { Display.new } register(:firmware) { Firmware.new } register(:log) { Log.new } + register(:models) { Model.new } register(:setup) { Setup.new } end end diff --git a/lib/trmnl/api/endpoints/model.rb b/lib/trmnl/api/endpoints/model.rb new file mode 100644 index 0000000..6cc872b --- /dev/null +++ b/lib/trmnl/api/endpoints/model.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require "inspectable" +require "pipeable" + +module TRMNL + module API + module Endpoints + # Handles API request/response. + class Model + include TRMNL::API::Dependencies[ + :requester, + contract: "contracts.model", + model: "models.model" + ] + + include Inspectable[contract: :class] + include Pipeable + + def call + pipe( + requester.get("models"), + try(:parse, catch: JSON::ParserError), + validate(contract, as: :to_h), + as(:fetch, :data), + map { |data| model.for(**data) } + ) + end + end + end + end +end diff --git a/spec/lib/trmnl/api/endpoints/model_spec.rb b/spec/lib/trmnl/api/endpoints/model_spec.rb new file mode 100644 index 0000000..2953c4a --- /dev/null +++ b/spec/lib/trmnl/api/endpoints/model_spec.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe TRMNL::API::Endpoints::Model do + subject(:endpoint) { described_class.new requester: } + + include_context "with application dependencies" + + let(:requester) { TRMNL::API::Requester.new http: } + + describe "#call" do + context "with success" do + let :http do + HTTP::Fake::Client.new do + get "/api/models" do + headers["Content-Type"] = "application/json" + status 200 + + <<~JSON + { + "data": [ + { + "name": "test", + "label": "Test", + "description": "A test.", + "colors": 2, + "bit_depth": 1, + "scale_factor": 1, + "rotation": 90, + "mime_type": "image/png", + "width": 800, + "height": 480, + "offset_x": 10, + "offset_y": 15, + "published_at": "2025-07-16T18:18:11+00:00" + } + ] + } + JSON + end + end + end + + it "answers response" do + result = endpoint.call + expect(result).to be_success( + [ + TRMNL::API::Models::Model[ + name: "test", + label: "Test", + description: "A test.", + colors: 2, + bit_depth: 1, + scale_factor: 1, + rotation: 90, + mime_type: "image/png", + width: 800, + height: 480, + offset_x: 10, + offset_y: 15, + published_at: Time.parse("2025-07-16T18:18:11+00:00") + ] + ] + ) + end + end + + context "with failure" do + let :http do + HTTP::Fake::Client.new do + get "/api/models" do + headers["Content-Type"] = "application/json" + status 404 + + <<~JSON + {"error": "Danger!"} + JSON + end + end + end + + it "answers failure response" do + result = described_class.new(requester:).call + expect(result).to match(Failure(be_a(HTTP::Response))) + end + end + end + + describe "#inspect" do + it_behaves_like "an inspectable endpoint", described_class.new + end +end