sync_sso() adds its own "custom." prefix. This results in "custom.custom.field" in the payload (#190)

sync_sso() adds its own “custom.” prefix. This results in “custom.custom.field” in the payload (#190)

  • DiscourseApi::SingleSignOn.custom_fields adds its own custom. prefix before sending the payload sync_sso expected the params to have the prefix in the params This results in custom.custom.field in the payload

    • I make this look good (••) / ( ••)>⌐■-■ / (⌐■_■)
    • Readme.md
    • Style
    • Use #sub instead of #gsub. Remove only first instance of custom.
    • Updated spec
    • More spec updates
    • SingleSignOn.unsigned_payload no longer prepends its own custom.
    • WIP
    • New parse_hash init method for DiscourseApi::SingleSignOn
  • Updated sync_sso()

  • custom_fields params input changed

    • Fixed rubocop
    • Readme
diff --git a/README.md b/README.md
index 34e3552..37421e1 100644
--- a/README.md
+++ b/README.md
@@ -52,7 +52,10 @@ client.sync_sso(                                #=> Synchronizes the SSO record
   name: "Test Name",
   username: "test_name",
   email: "name@example.com",
-  external_id: "2"
+  external_id: "2",
+  custom_fields: {
+    field_1: 'potato'
+  }
 )
 
 # Private messages
diff --git a/lib/discourse_api/api/sso.rb b/lib/discourse_api/api/sso.rb
index 06c2e01..a10d3e4 100644
--- a/lib/discourse_api/api/sso.rb
+++ b/lib/discourse_api/api/sso.rb
@@ -3,24 +3,7 @@ module DiscourseApi
   module API
     module SSO
       def sync_sso(params = {})
-        sso = DiscourseApi::SingleSignOn.new
-        sso.sso_secret = params[:sso_secret]
-        sso.name = params[:name]
-        sso.username = params[:username]
-        sso.email = params[:email]
-        sso.external_id = params[:external_id]
-        sso.suppress_welcome_message = params[:suppress_welcome_message] === true
-        sso.avatar_url = params[:avatar_url]
-        sso.profile_background_url = params[:profile_background_url]
-        sso.card_background_url = params[:card_background_url]
-        sso.bio = params[:bio]
-        sso.title = params[:title]
-        sso.avatar_force_update = params[:avatar_force_update] === true
-        sso.add_groups = params[:add_groups]
-        sso.remove_groups = params[:remove_groups]
-        params.keys.select { |key| key.to_s.start_with?("custom") }.each do |custom_key|
-          sso.custom_fields[custom_key] = params[custom_key]
-        end
+        sso = DiscourseApi::SingleSignOn.parse_hash(params)
 
         post("/admin/users/sync_sso", sso.payload)
       end
diff --git a/lib/discourse_api/single_sign_on.rb b/lib/discourse_api/single_sign_on.rb
index 7419b8f..4c560ff 100644
--- a/lib/discourse_api/single_sign_on.rb
+++ b/lib/discourse_api/single_sign_on.rb
@@ -15,6 +15,7 @@ module DiscourseApi
     #NONCE_EXPIRY_TIME = 10.minutes # minutes is a rails method and is causing an error. Is this needed in the api?
 
     attr_accessor(*ACCESSORS)
+    attr_accessor :custom_fields
     attr_writer :sso_secret, :sso_url
 
     def self.sso_secret
@@ -25,6 +26,36 @@ module DiscourseApi
       raise RuntimeError, "sso_url not implemented on class, be sure to set it on instance"
     end
 
+    def self.parse_hash(payload)
+      payload
+      sso = new
+
+      sso.sso_secret = payload.delete(:sso_secret)
+      sso.sso_url = payload.delete(:sso_url)
+
+      ACCESSORS.each do |k|
+        val = payload[k]
+
+        val = val.to_i if FIXNUMS.include? k
+        if BOOLS.include? k
+          val = ["true", "false"].include?(val) ? val == "true" : nil
+        end
+        val = Array(val) if ARRAYS.include?(k) && !val.nil?
+        sso.send("#{k}=", val)
+      end
+      # Set custom_fields
+      sso.custom_fields = payload[:custom_fields]
+      # Add custom_fields (old format)
+      payload.each do |k, v|
+        if field = k[/^custom\.(.+)$/, 1]
+          # Maintain adding of .custom bug
+          sso.custom_fields["custom.#{field}"] = v
+        end
+      end
+
+      sso
+    end
+
     def self.parse(payload, sso_secret = nil)
       sso = new
       sso.sso_secret = sso_secret if sso_secret
@@ -107,7 +138,5 @@ module DiscourseApi
 
       Rack::Utils.build_query(payload)
     end
-
   end
-
 end
diff --git a/spec/discourse_api/api/sso_spec.rb b/spec/discourse_api/api/sso_spec.rb
index 5750b9b..95cebc3 100644
--- a/spec/discourse_api/api/sso_spec.rb
+++ b/spec/discourse_api/api/sso_spec.rb
@@ -4,9 +4,51 @@ require 'spec_helper'
 describe DiscourseApi::API::SSO do
   subject { DiscourseApi::Client.new("#{host}", "test_d7fd0429940", "test_user") }
 
+  let(:params) do
+    {
+      sso_secret: 'abc',
+      sso_url: 'www.google.com',
+      name: 'Some User',
+      username: 'some_user',
+      email: 'some@email.com',
+      external_id: 'abc',
+      suppress_welcome_message: false,
+      avatar_url: 'https://www.website.com',
+      title: 'ruby',
+      avatar_force_update: false,
+      add_groups: ['a', 'b'],
+      remove_groups: ['c', 'd'],
+      # old format (which results in custom.custom.field_1 in unsigned_payload)
+      'custom.field_1' => 'tomato',
+      # new format
+      custom_fields: {
+        field_2: 'potato'
+      }
+    }
+  end
+  let(:expected_unsigned_payload) do
+    'name=Some+User&username=some_user&email=some%40email.com&'\
+    'avatar_url=https%3A%2F%2Fwww.website.com&external_id=abc&title=ruby'\
+    '&add_groups=a&add_groups=b&remove_groups=c&remove_groups=d&custom.field_2=potato&'\
+    'custom.custom.field_1=tomato'
+  end
+  let(:sso_double) { DiscourseApi::SingleSignOn.parse_hash(params) }
+
   describe "#sync_sso" do
     before do
-      stub_post(/.*sync_sso.*/).to_return(body: fixture("user.json"), headers: { content_type: "application/json" })
+      stub_post(/.*sync_sso.*/).to_return(
+        body: fixture("user.json"),
+        headers: { content_type: "application/json" }
+      )
+    end
+
+    it 'assigns params to sso instance' do
+      allow(DiscourseApi::SingleSignOn).to(receive(:parse_hash).with(params).and_return(sso_double))
+
+      subject.sync_sso(params)
+
+      expect(sso_double.custom_fields).to eql({ 'custom.field_1' => 'tomato', :field_2 => 'potato' })
+      expect(sso_double.unsigned_payload).to eql(expected_unsigned_payload)
     end
 
     it "requests the correct resource" do

GitHub sha: 9587ed75

This commit appears in #190 which was merged by blake.