Commit c568e47

mo khan <mo@mokhan.ca>
2026-07-27 17:49:35
refactor: slim down the CLI API
cli
1 parent 3cb129a
lib/scim/kit/cli/schemas/schemas.schema.json
@@ -27,7 +27,7 @@
           "type": "string",
           "enum": [
             "string", "boolean", "decimal", "integer", "dateTime",
-            "reference", "complex"
+            "reference", "binary", "complex"
           ]
         },
         "multiValued": { "type": "boolean" },
lib/scim/kit/cli/app.rb
@@ -45,29 +45,20 @@ module Scim
         private
 
         def fetch_discovery
-          responses, error = fetch_discovery_responses
-          return Reporting.report(error, shell) if error
-
-          combined = Http::Result.new(200, responses)
-          report_discovery(combined, responses)
-        end
-
-        def fetch_discovery_responses
           responses = {}
           RESOURCES.each do |key, path|
-            uri = Cli.join_uri(url, path)
-            result = http.fetch(uri, headers: headers)
-            return [responses, result] unless result.ok?
+            result = http.fetch(Cli.join_uri(url, path), headers: headers)
+            return Reporting.report(result, shell) unless result.ok?
 
             responses[key] = result.body
           end
-          [responses, nil]
+          report_discovery(Http::Result.new(200, responses))
         end
 
-        def report_discovery(combined, responses)
+        def report_discovery(combined)
           if options[:validate]
             Reporting.report_with_validation(
-              combined, shell, discovery_errors(responses)
+              combined, shell, discovery_errors(combined.body)
             )
           else
             Reporting.report(combined, shell)
@@ -98,7 +89,7 @@ module Scim
             uri.query = URI.encode_www_form(attributes: options[:attributes])
           end
           result = http.fetch(uri, headers: headers)
-          validate_and_report(result, resource_type) { |schema| schema }
+          validate_and_report(result, resource_type)
         end
 
         def resolve_endpoint(resource_type)
@@ -137,7 +128,8 @@ module Scim
             return Reporting.report(result, shell)
           end
 
-          errors = Validator.errors_for(yield(schema), result.body)
+          schema = yield(schema) if block_given?
+          errors = Validator.errors_for(schema, result.body)
           Reporting.report_with_validation(result, shell, errors)
         end
 
lib/scim/kit/cli/resource_type_resolver.rb
@@ -20,13 +20,6 @@ module Scim
           match
         end
 
-        def endpoint_for(name)
-          endpoint = resource_type_for(name)[:endpoint]
-          raise MissingEndpoint, name if endpoint.to_s.empty?
-
-          endpoint
-        end
-
         private
 
         attr_reader :http, :base_url, :headers
spec/scim/kit/cli/app_spec.rb
@@ -37,6 +37,21 @@ RSpec.describe Scim::Kit::Cli::App do
           .to output("#{JSON.pretty_generate(detail: 'boom')}\n").to_stderr
       end
     end
+
+    context 'when the matched resource type has no endpoint' do
+      let(:resource_types) { [{ id: 'User', name: 'User' }] }
+
+      it 'reports a MissingEndpoint error' do
+        expect { exit_status { call.call(app, 'User') } }
+          .to output(/User/).to_stderr
+      end
+
+      it 'exits 1' do
+        allow($stderr).to receive(:print)
+
+        expect(exit_status { call.call(app, 'User') }).to eq(1)
+      end
+    end
   end
 
   describe '#discover' do
spec/scim/kit/cli/resource_type_resolver_spec.rb
@@ -6,45 +6,14 @@ RSpec.describe Scim::Kit::Cli::ResourceTypeResolver do
   let(:base_url) { FFaker::Internet.uri('https') }
   let(:headers) { {} }
 
-  describe '#endpoint_for' do
-    before do
-      stub_request(:get, "#{base_url}/ResourceTypes").to_return(
-        status: 200,
-        body: [
-          { id: 'User', name: 'User', endpoint: '/Users' },
-          { id: 'Group', name: 'Group', endpoint: '/Groups' }
-        ].to_json
-      )
-    end
-
-    specify { expect(subject.endpoint_for('User')).to eql('/Users') }
-    specify { expect(subject.endpoint_for('user')).to eql('/Users') }
-    specify { expect(subject.endpoint_for('Group')).to eql('/Groups') }
-
-    it 'raises when no resource type matches the given name' do
-      expect { subject.endpoint_for('Nope') }.to raise_error(
-        Scim::Kit::Cli::UnknownResourceType, /Nope/
-      )
-    end
-
-    context 'with custom headers' do
-      let(:headers) { { 'Authorization' => 'Bearer xyz' } }
-
-      it 'forwards them to the ResourceTypes request' do
-        subject.endpoint_for('User')
-
-        expect(a_request(:get, "#{base_url}/ResourceTypes").with(headers: headers)).to have_been_made
-      end
-    end
-  end
-
   describe '#resource_type_for' do
     before do
       stub_request(:get, "#{base_url}/ResourceTypes").to_return(
         status: 200,
         body: [
           { id: 'User', name: 'User', endpoint: '/Users',
-            schema: 'urn:ietf:params:scim:schemas:core:2.0:User' }
+            schema: 'urn:ietf:params:scim:schemas:core:2.0:User' },
+          { id: 'Group', name: 'Group', endpoint: '/Groups' }
         ].to_json
       )
     end
@@ -56,25 +25,24 @@ RSpec.describe Scim::Kit::Cli::ResourceTypeResolver do
       )
     end
 
+    it 'matches case-insensitively' do
+      expect(subject.resource_type_for('user')).to include(endpoint: '/Users')
+    end
+
     it 'raises when no resource type matches the given name' do
       expect { subject.resource_type_for('Nope') }.to raise_error(
         Scim::Kit::Cli::UnknownResourceType, /Nope/
       )
     end
-  end
 
-  context 'when the matched resource type has no endpoint' do
-    before do
-      stub_request(:get, "#{base_url}/ResourceTypes").to_return(
-        status: 200,
-        body: [{ id: 'User', name: 'User' }].to_json
-      )
-    end
+    context 'with custom headers' do
+      let(:headers) { { 'Authorization' => 'Bearer xyz' } }
 
-    it 'raises MissingEndpoint' do
-      expect { subject.endpoint_for('User') }.to raise_error(
-        Scim::Kit::Cli::MissingEndpoint, /User/
-      )
+      it 'forwards them to the ResourceTypes request' do
+        subject.resource_type_for('User')
+
+        expect(a_request(:get, "#{base_url}/ResourceTypes").with(headers: headers)).to have_been_made
+      end
     end
   end
 
@@ -82,7 +50,7 @@ RSpec.describe Scim::Kit::Cli::ResourceTypeResolver do
     before { stub_request(:get, "#{base_url}/ResourceTypes").to_return(status: 500, body: '{}') }
 
     it 'raises' do
-      expect { subject.endpoint_for('User') }.to raise_error(Scim::Kit::Cli::RequestFailed)
+      expect { subject.resource_type_for('User') }.to raise_error(Scim::Kit::Cli::RequestFailed)
     end
   end
 
@@ -95,7 +63,7 @@ RSpec.describe Scim::Kit::Cli::ResourceTypeResolver do
     end
 
     it 'raises InvalidResponse' do
-      expect { subject.endpoint_for('User') }.to raise_error(Scim::Kit::Cli::InvalidResponse)
+      expect { subject.resource_type_for('User') }.to raise_error(Scim::Kit::Cli::InvalidResponse)
     end
   end
 end