From 30390e8264ea4206abd1a8aa124a335d1c263c09 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Tue, 13 Aug 2019 14:11:46 +1000 Subject: [PATCH] Improve flexibility and structure of send_to_api action --- .../controllers/admin-wizards-api.js.es6 | 18 +++--- .../discourse/templates/admin-wizards-api.hbs | 34 ++++++---- assets/stylesheets/wizard_custom_admin.scss | 17 +++-- config/locales/client.en.yml | 2 + lib/api/authorization.rb | 6 +- lib/api/endpoint.rb | 63 +++++++++++-------- lib/api/{logentry.rb => log_entry.rb} | 0 lib/builder.rb | 2 +- plugin.rb | 2 +- serializers/api/endpoint_serializer.rb | 4 +- 10 files changed, 92 insertions(+), 56 deletions(-) rename lib/api/{logentry.rb => log_entry.rb} (100%) diff --git a/assets/javascripts/discourse/controllers/admin-wizards-api.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-api.js.es6 index 42451421..371ad14e 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-api.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-api.js.es6 @@ -11,6 +11,8 @@ export default Ember.Controller.extend({ showRemove: Ember.computed.not('isNew'), showRedirectUri: Ember.computed.and('threeLeggedOauth', 'api.name'), responseIcon: null, + contentTypes: ['application/json', 'application/x-www-form-urlencoded'], + successCodes: [100, 101, 102, 200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301, 302, 303, 303, 304, 305, 306, 307, 308], @computed('saveDisabled', 'api.authType', 'api.authUrl', 'api.tokenUrl', 'api.clientId', 'api.clientSecret', 'threeLeggedOauth') authDisabled(saveDisabled, authType, authUrl, tokenUrl, clientId, clientSecret, threeLeggedOauth) { @@ -24,7 +26,7 @@ export default Ember.Controller.extend({ return !name || !authType; }, - authorizationTypes: ['basic', 'oauth_2', 'oauth_3'], + authorizationTypes: ['none', 'basic', 'oauth_2', 'oauth_3'], isBasicAuth: Ember.computed.equal('api.authType', 'basic'), @computed('api.authType') @@ -124,13 +126,15 @@ export default Ember.Controller.extend({ requiredParams = ['authUrl', 'tokenUrl', 'clientId', 'clientSecret']; } - for (let rp of requiredParams) { - if (!api[rp]) { - let key = rp.replace('auth', ''); - error = `${I18n.t(`admin.wizard.api.auth.${key.underscore()}`)} is required for ${authType}`; - break; + if (requiredParams) { + for (let rp of requiredParams) { + if (!api[rp]) { + let key = rp.replace('auth', ''); + error = `${I18n.t(`admin.wizard.api.auth.${key.underscore()}`)} is required for ${authType}`; + break; + } + data[rp.underscore()] = api[rp]; } - data[rp.underscore()] = api[rp]; } const params = api.authParams; diff --git a/assets/javascripts/discourse/templates/admin-wizards-api.hbs b/assets/javascripts/discourse/templates/admin-wizards-api.hbs index 160225ea..611876a0 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-api.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-api.hbs @@ -228,18 +228,28 @@
  • - {{input value=endpoint.name - placeholder=(i18n 'admin.wizard.api.endpoint.name')}} - {{combo-box content=endpointMethods - value=endpoint.method - none="admin.wizard.api.endpoint.method"}} - {{input value=endpoint.url - placeholder=(i18n 'admin.wizard.api.endpoint.url') - class='endpoint-url'}} - {{d-button action=(action "removeEndpoint") - actionParam=endpoint - icon='times' - class='remove-endpoint'}} +
    + {{input value=endpoint.name + placeholder=(i18n 'admin.wizard.api.endpoint.name')}} + {{input value=endpoint.url + placeholder=(i18n 'admin.wizard.api.endpoint.url') + class='endpoint-url'}} + {{d-button action=(action "removeEndpoint") + actionParam=endpoint + icon='times' + class='remove-endpoint'}} +
    +
    + {{combo-box content=endpointMethods + value=endpoint.method + none="admin.wizard.api.endpoint.method"}} + {{combo-box content=contentTypes + value=endpoint.content_type + none="admin.wizard.api.endpoint.content_type"}} + {{multi-select content=successCodes + values=endpoint.success_codes + none="admin.wizard.api.endpoint.success_codes"}} +
  • diff --git a/assets/stylesheets/wizard_custom_admin.scss b/assets/stylesheets/wizard_custom_admin.scss index 7ae598ac..3a2cd794 100644 --- a/assets/stylesheets/wizard_custom_admin.scss +++ b/assets/stylesheets/wizard_custom_admin.scss @@ -446,14 +446,19 @@ } .endpoint { - display: flex; margin-top: 20px; - + + .top, .bottom { + display: flex; + } + + .top { + margin-bottom: 15px; + } + .combo-box { - width: 200px; margin-right: 10px; - margin-top: -2px; - width: 150px; + width: 210px; } input { @@ -462,7 +467,7 @@ } .endpoint-url { - width: 300px; + flex: 1 1 auto; } .remove-endpoint { diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index d65c42c0..ce47502b 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -211,6 +211,8 @@ en: name: "Endpoint name" method: "Select a method" url: "Enter a url" + content_type: "Select a content type" + success_codes: "Select success codes" log: label: "Logs" diff --git a/lib/api/authorization.rb b/lib/api/authorization.rb index 745ecc9b..1bfa4a29 100644 --- a/lib/api/authorization.rb +++ b/lib/api/authorization.rb @@ -64,7 +64,7 @@ class CustomWizard::Api::Authorization PluginStore.remove("custom_wizard_api_#{api_name}", "authorization") end - def self.get_header_authorization_string(name) + def self.authorization_string(name) auth = CustomWizard::Api::Authorization.get(name) raise Discourse::InvalidParameters.new(:name) unless auth.present? @@ -72,9 +72,11 @@ class CustomWizard::Api::Authorization raise Discourse::InvalidParameters.new(:username) unless auth.username.present? raise Discourse::InvalidParameters.new(:password) unless auth.password.present? "Basic #{Base64.strict_encode64((auth.username + ":" + auth.password).chomp)}" - else + elsif ['oauth_3', 'oauth_2'].include?(auth.auth_type) raise Discourse::InvalidParameters.new(auth.access_token) unless auth.access_token.present? "Bearer #{auth.access_token}" + else + nil end end diff --git a/lib/api/endpoint.rb b/lib/api/endpoint.rb index 3036a49f..f77a49e6 100644 --- a/lib/api/endpoint.rb +++ b/lib/api/endpoint.rb @@ -5,7 +5,9 @@ class CustomWizard::Api::Endpoint :name, :api_name, :method, - :url + :url, + :content_type, + :success_codes def initialize(api_name, data={}) @api_name = api_name @@ -62,40 +64,49 @@ class CustomWizard::Api::Endpoint end end - def self.request(user, api_name, endpoint_id, body) + def self.request(user = Discourse.system_user, api_name, endpoint_id, body) endpoint = self.get(api_name, endpoint_id) - auth = CustomWizard::Api::Authorization.get_header_authorization_string(api_name) + auth_string = CustomWizard::Api::Authorization.authorization_string(api_name) + content_type = endpoint.content_type + + headers = {} + headers["Authorization"] = auth_string if auth_string + headers["Content-Type"] = content_type if content_type - connection = Excon.new( - URI.parse(URI.encode(endpoint.url)).to_s, - :headers => { - "Authorization" => auth, - "Accept" => "application/json, */*", - "Content-Type" => "application/json" - } - ) + connection = Excon.new(URI.parse(URI.encode(endpoint.url)).to_s, headers: headers) - params = { - method: endpoint.method - } + params = { method: endpoint.method } if body - body = JSON.generate(body) - body.delete! '\\' + if content_type === "application/json" + body = JSON.generate(body) + elsif content_type === "application/x-www-form-urlencoded" + body = URI.encode_www_form(body) + end + params[:body] = body end - begin - response = connection.request(params) - log_params = {time: Time.now, user_id: user.id, status: 'SUCCESS', url: endpoint.url, error: ""} + response = connection.request(params) + + if endpoint.success_codes.include?(response.status) + begin + result = JSON.parse(response.body) + rescue JSON::ParserError + result = response.body + end - CustomWizard::Api::LogEntry.set(api_name, log_params) - return JSON.parse(response.body) - rescue - # TODO: improve error detail - log_params = {time: Time.now, user_id: user.id, status: 'FAILURE', url: endpoint.url, error: "API request failed"} - CustomWizard::Api::LogEntry.set(api_name, log_params) - return JSON.parse "[{\"error\":\"API request failed\"}]" + CustomWizard::Api::LogEntry.set(api_name, log_params(user, 'SUCCESS', endpoint.url)) + + result + else + message = "API request failed" + CustomWizard::Api::LogEntry.set(api_name, log_params(user, 'FAIL', endpoint.url, message)) + { error: message } end end + + def self.log_params(user, status, url, message = "") + { time: Time.now, user_id: user.id, status: status, url: url, error: message } + end end diff --git a/lib/api/logentry.rb b/lib/api/log_entry.rb similarity index 100% rename from lib/api/logentry.rb rename to lib/api/log_entry.rb diff --git a/lib/builder.rb b/lib/builder.rb index 1a9bbc7c..df8f2e4c 100644 --- a/lib/builder.rb +++ b/lib/builder.rb @@ -492,7 +492,7 @@ class CustomWizard::Builder rescue JSON::ParserError raise Discourse::InvalidParameters, "Invalid API body definition: #{action['api_body']} for #{action['title']}" end - api_body = CustomWizard::Builder.fill_placeholders(JSON.generate(api_body_parsed), user, data) + api_body = JSON.parse(CustomWizard::Builder.fill_placeholders(JSON.generate(api_body_parsed), user, data)) end result = CustomWizard::Api::Endpoint.request(user, action['api'], action['api_endpoint'], api_body) diff --git a/plugin.rb b/plugin.rb index 1acacf99..3f95e2ea 100644 --- a/plugin.rb +++ b/plugin.rb @@ -101,7 +101,7 @@ after_initialize do load File.expand_path('../lib/api/api.rb', __FILE__) load File.expand_path('../lib/api/authorization.rb', __FILE__) load File.expand_path('../lib/api/endpoint.rb', __FILE__) - load File.expand_path('../lib/api/logentry.rb', __FILE__) + load File.expand_path('../lib/api/log_entry.rb', __FILE__) load File.expand_path('../controllers/api.rb', __FILE__) load File.expand_path('../serializers/api/api_serializer.rb', __FILE__) load File.expand_path('../serializers/api/authorization_serializer.rb', __FILE__) diff --git a/serializers/api/endpoint_serializer.rb b/serializers/api/endpoint_serializer.rb index 18c1406c..31d0f975 100644 --- a/serializers/api/endpoint_serializer.rb +++ b/serializers/api/endpoint_serializer.rb @@ -2,7 +2,9 @@ class CustomWizard::Api::EndpointSerializer < ApplicationSerializer attributes :id, :name, :method, - :url + :url, + :content_type, + :success_codes def method object.send('method')