From f331f80cbbe3003fa254a1d87519268c20ec11c8 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Sun, 2 Jun 2019 20:54:31 +1000 Subject: [PATCH] Update wizard api CRUD - New api metadata model - New api id system - Minor UI updates --- .travis.yml | 2 +- .../controllers/admin-wizards-api.js.es6 | 76 +++++++-- .../controllers/admin-wizards-apis.js.es6 | 3 + .../custom-wizard-admin-route-map.js.es6 | 2 +- .../discourse/models/custom-wizard-api.js.es6 | 17 +- .../discourse/routes/admin-wizards-api.js.es6 | 12 +- .../routes/admin-wizards-apis.js.es6 | 20 +++ .../discourse/templates/admin-wizards-api.hbs | 157 ++++++++++-------- .../templates/admin-wizards-apis.hbs | 8 +- assets/stylesheets/wizard_custom_admin.scss | 44 ++++- config/locales/client.en.yml | 57 ++++--- controllers/api.rb | 95 +++++++---- jobs/refresh_api_access_token.rb | 2 +- lib/api/api.rb | 26 ++- lib/api/authorization.rb | 67 ++++---- lib/api/endpoint.rb | 34 ++-- plugin.rb | 7 +- serializers/api/api_serializer.rb | 23 ++- serializers/api/basic_api_serializer.rb | 2 +- 19 files changed, 446 insertions(+), 208 deletions(-) create mode 100644 assets/javascripts/discourse/controllers/admin-wizards-apis.js.es6 diff --git a/.travis.yml b/.travis.yml index 01ff46d8..b48ce894 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ # Uncomment tests runner when tests are added. sudo: required -#services: +#names: #- docker before_install: diff --git a/assets/javascripts/discourse/controllers/admin-wizards-api.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-api.js.es6 index ec4b5170..832bf1c4 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-api.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-api.js.es6 @@ -1,13 +1,32 @@ import { ajax } from 'discourse/lib/ajax'; import { popupAjaxError } from 'discourse/lib/ajax-error'; import CustomWizardApi from '../models/custom-wizard-api'; +import { default as computed, observes } from 'ember-addons/ember-computed-decorators'; +import DiscourseURL from 'discourse/lib/url'; export default Ember.Controller.extend({ + queryParams: ['refresh_list'], loadingSubscriptions: false, notAuthorized: Ember.computed.not('api.authorized'), authorizationTypes: ['oauth', 'basic'], isOauth: Ember.computed.equal('api.authType', 'oauth'), endpointMethods: ['GET', 'PUT', 'POST', 'PATCH', 'DELETE'], + saveDisabled: Ember.computed.empty('api.name'), + showRemove: Ember.computed.not('isNew'), + + @computed('saveDisabled', 'authType', 'authUrl') + authDisabled(saveDisabled, authType, authUrl) { + return saveDisabled || !authType || !authUrl; + }, + + @observes('api.title') + titleWatcher() { + const title = this.get('api.title'); + + if (this.get('originalTitle')) { + this.set('originalTitle', title); + } + }, actions: { addParam() { @@ -48,12 +67,25 @@ export default Ember.Controller.extend({ save() { const api = this.get('api'); - const service = api.service; + const name = api.name; + let refreshList = false; - let data = {}; + if (!name ) return; - data['auth_type'] = api.authType; - data['auth_url'] = api.authUrl; + let data = { + title: api.title + }; + + if (this.get('isNew') || (api.title !== this.get('originalTitle'))) { + refreshList = true; + } + + if (api.get('isNew')) { + data['new'] = true; + }; + + if (api.authType) data['auth_type'] = api.authType; + if (api.authUrl) data['auth_url'] = api.authUrl; if (data.auth_type === 'oauth') { data['client_id'] = api.clientId; @@ -72,23 +104,43 @@ export default Ember.Controller.extend({ } const endpoints = api.endpoints; - if (endpoints != undefined) { - if (endpoints.length) { - data['endpoints'] = JSON.stringify(endpoints); - } + if (endpoints.length) { + data['endpoints'] = JSON.stringify(endpoints); } - this.set('savingApi', true); + this.set('updating', true); - ajax(`/admin/wizards/apis/${service}`, { + ajax(`/admin/wizards/apis/${name.underscore()}`, { type: 'PUT', data }).catch(popupAjaxError) .then(result => { if (result.success) { - this.set('api', CustomWizardApi.create(result.api)); + if (refreshList) { + this.transitionToRoute('adminWizardsApi', result.api.name.dasherize()).then(() => { + this.send('refreshModel'); + }); + } else { + this.set('api', CustomWizardApi.create(result.api)); + } } - }).finally(() => this.set('savingApi', false)); + }).finally(() => this.set('updating', false)); + }, + + remove() { + const name = this.get('api.name'); + if (!name) return; + + this.set('updating', true); + + ajax(`/admin/wizards/apis/${name.underscore()}`, { + type: 'DELETE' + }).catch(popupAjaxError) + .then(result => { + if (result.success) { + DiscourseURL.routeTo('/admin/wizards/apis?refresh=true'); + } + }).finally(() => this.set('updating', false)); } } }); diff --git a/assets/javascripts/discourse/controllers/admin-wizards-apis.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-apis.js.es6 new file mode 100644 index 00000000..52748bc8 --- /dev/null +++ b/assets/javascripts/discourse/controllers/admin-wizards-apis.js.es6 @@ -0,0 +1,3 @@ +export default Ember.Controller.extend({ + queryParams: ['refresh'] +}); diff --git a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 index dfe53a82..f5022153 100644 --- a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 +++ b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 @@ -9,7 +9,7 @@ export default { this.route('adminWizardSubmissions', { path: '/:wizard_id', resetNamespace: true }); }); this.route('adminWizardsApis', { path: '/apis', resetNamespace: true }, function() { - this.route('adminWizardsApi', { path: '/:service', resetNamespace: true }); + this.route('adminWizardsApi', { path: '/:name', resetNamespace: true }); }); }); } diff --git a/assets/javascripts/discourse/models/custom-wizard-api.js.es6 b/assets/javascripts/discourse/models/custom-wizard-api.js.es6 index 6817ae70..5f051cc9 100644 --- a/assets/javascripts/discourse/models/custom-wizard-api.js.es6 +++ b/assets/javascripts/discourse/models/custom-wizard-api.js.es6 @@ -2,10 +2,11 @@ import { ajax } from 'discourse/lib/ajax'; import { default as computed } from 'ember-addons/ember-computed-decorators'; const CustomWizardApi = Discourse.Model.extend({ - @computed('service') - redirectUri(service) { + @computed('name') + redirectUri(name) { + let nameParam = name.toString().dasherize(); const baseUrl = location.protocol+'//'+location.hostname+(location.port ? ':'+location.port: ''); - return baseUrl + `/admin/wizards/apis/${service}/redirect`; + return baseUrl + `/admin/wizards/apis/${nameParam}/redirect`; } }); @@ -16,7 +17,8 @@ CustomWizardApi.reopenClass({ const endpoints = params.endpoints; api.setProperties({ - service: params.service, + name: params.name, + title: params.title, authType: authorization.auth_type, authUrl: authorization.auth_url, tokenUrl: authorization.token_url, @@ -29,14 +31,15 @@ CustomWizardApi.reopenClass({ code: authorization.code, tokenExpiresAt: authorization.token_expires_at, tokenRefreshAt: authorization.token_refresh_at, - endpoints: Ember.A(endpoints) + endpoints: Ember.A(endpoints), + isNew: params.isNew }); return api; }, - find(service) { - return ajax(`/admin/wizards/apis/${service}`, { + find(name) { + return ajax(`/admin/wizards/apis/${name}`, { type: 'GET' }).then(result => { return CustomWizardApi.create(result); diff --git a/assets/javascripts/discourse/routes/admin-wizards-api.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-api.js.es6 index dda7d416..58f624b5 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-api.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-api.js.es6 @@ -1,11 +1,17 @@ import CustomWizardApi from '../models/custom-wizard-api'; export default Discourse.Route.extend({ + queryParams: { + refresh_list: { + refreshModel: true + } + }, + model(params) { - if (params.service === 'new') { - return CustomWizardApi.create(); + if (params.name === 'new') { + return CustomWizardApi.create({ isNew: true }); } else { - return CustomWizardApi.find(params.service); + return CustomWizardApi.find(params.name); } }, diff --git a/assets/javascripts/discourse/routes/admin-wizards-apis.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-apis.js.es6 index b7e89e10..54174c6b 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-apis.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-apis.js.es6 @@ -5,7 +5,27 @@ export default Discourse.Route.extend({ return CustomWizardApi.list(); }, + afterModel(model) { + const apiParams = this.paramsFor('admin-wizards-api'); + + if (model.length) { + if (!apiParams.name) { + this.transitionTo('adminWizardsApi', model[0].name.dasherize()); + } else { + return; + } + } else { + this.transitionTo('adminWizardsApi', 'new'); + } + }, + setupController(controller, model){ controller.set("model", model); + }, + + actions: { + refreshModel() { + this.refresh(); + } } }); diff --git a/assets/javascripts/discourse/templates/admin-wizards-api.hbs b/assets/javascripts/discourse/templates/admin-wizards-api.hbs index f91a4c3f..a17a0c0b 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-api.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-api.hbs @@ -1,51 +1,77 @@ -
-
- {{input value=api.service placeholder=(i18n 'admin.wizard.api.service')}} +
+
- {{#if savingApi}} + {{#if updating}} {{loading-spinner size="small"}} {{/if}} - {{d-button label="admin.wizard.api.save" action="save" class="btn-primary"}} - {{d-button action="removeApi" label="admin.wizard.api.remove"}} + + {{d-button label="admin.wizard.api.save" action="save" class="btn-primary" disabled=saveDisabled}} + + {{#if showRemove}} + {{d-button action="remove" label="admin.wizard.api.remove"}} + {{/if}}
-
- {{i18n 'admin.wizard.api.auth'}} +
+
+ {{i18n 'admin.wizard.api.auth.label'}} +
+ +
+ {{d-button label="admin.wizard.api.auth.btn" action="authorize" disabled=authDisabled class="btn-primary"}} +
- {{i18n 'admin.wizard.api.auth_settings'}} + {{i18n 'admin.wizard.api.auth.settings'}}
-
-
- {{i18n 'admin.wizard.api.redirect_uri'}} - {{api.redirectUri}} + {{#if api.name}} +
+
+ +
+ {{api.redirectUri}} +
+
-
+ {{/if}} -
- +
+
- {{combo-box value=api.authType content=authorizationTypes none='admin.wizard.api.auth_type_none'}} + {{combo-box value=api.authType content=authorizationTypes none='admin.wizard.api.auth.type_none'}}
- +
{{input value=api.authUrl}}
- +
{{input value=api.tokenUrl}}
@@ -53,85 +79,82 @@ {{#if isOauth}}
- +
{{input value=api.clientId}}
- +
{{input value=api.clientSecret}}
- +
{{#each api.authParams as |param|}}
- {{input value=param.key placeholder=(i18n 'admin.wizard.api.param_key')}} - {{input value=param.value placeholder=(i18n 'admin.wizard.api.param_value')}} + {{input value=param.key placeholder=(i18n 'admin.wizard.api.auth.params.key')}} + {{input value=param.value placeholder=(i18n 'admin.wizard.api.auth.params.value')}} {{d-button action='removeParam' actionParam=param icon='times'}}
{{/each}} - {{d-button label='admin.wizard.api.param_new' icon='plus' action='addParam'}} + {{d-button label='admin.wizard.api.auth.params.new' icon='plus' action='addParam'}}
{{/if}} - -
- {{d-button label="admin.wizard.api.authorize" action="authorize"}} -
-
- {{i18n 'admin.wizard.api.auth_status'}} -
-
{{#if api.authorized}} - {{i18n "admin.wizard.api.authorized"}} + {{i18n "admin.wizard.api.status.authorized"}} {{else}} - {{i18n "admin.wizard.api.not_authorized"}} + {{i18n "admin.wizard.api.status.not_authorized"}} {{/if}}
+ +
+ {{i18n 'admin.wizard.api.status.label'}} +
+
- + +
+ {{api.code}} +
+
+ +
+
{{api.accessToken}}
- -
- {{api.tokenExpiresAt}} -
-
- -
- -
- {{api.tokenRefreshAt}} -
-
- -
- +
{{api.refreshToken}}
- +
- {{api.code}} + {{api.tokenExpiresAt}} +
+
+ +
+ +
+ {{api.tokenRefreshAt}}
@@ -144,17 +167,19 @@
{{d-button action='addEndpoint' label='admin.wizard.api.endpoint.add' icon='plus'}} -
-
    - {{#each api.endpoints as |endpoint|}} -
  • -
    - {{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='removeEndpoint' actionParam=endpoint icon='times' class='remove-endpoint'}} -
    -
  • - {{/each}} -
-
+ {{#if api.endpoints}} +
+
    + {{#each api.endpoints as |endpoint|}} +
  • +
    + {{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='removeEndpoint' actionParam=endpoint icon='times' class='remove-endpoint'}} +
    +
  • + {{/each}} +
+
+ {{/if}}
diff --git a/assets/javascripts/discourse/templates/admin-wizards-apis.hbs b/assets/javascripts/discourse/templates/admin-wizards-apis.hbs index 78a5c954..c5f67660 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-apis.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-apis.hbs @@ -3,7 +3,13 @@
    {{#each model as |api|}}
  • - {{#link-to "adminWizardsApi" api.service}}{{api.service}}{{/link-to}} + {{#link-to "adminWizardsApi" (dasherize api.name)}} + {{#if api.title}} + {{api.title}} + {{else}} + {{api.name}} + {{/if}} + {{/link-to}}
  • {{/each}}
diff --git a/assets/stylesheets/wizard_custom_admin.scss b/assets/stylesheets/wizard_custom_admin.scss index 7bec453f..8026525c 100644 --- a/assets/stylesheets/wizard_custom_admin.scss +++ b/assets/stylesheets/wizard_custom_admin.scss @@ -308,12 +308,23 @@ .content-list { margin-right: 20px; } + + .new-api { + margin-top: 20px; + } + + .metadata .title input { + width: 400px; + } } .wizard-api-header { display: flex; justify-content: space-between; - margin-bottom: 20px; + + &.page { + margin-bottom: 20px; + } } .wizard-api-authentication { @@ -324,8 +335,37 @@ .settings { border-right: 1px solid #333; - margin-right: 10px; + margin-right: 20px; padding-right: 20px; + width: 50%; + max-width: 50%; + } + + .redirect-uri .controls { + word-break: break-all; + } + + .auth-type .select-kit { + min-width: 210px; + width: 210px; + margin-bottom: 10px; + } + + .status { + width: 50%; + max-width: 50%; + + .wizard-header { + overflow: hidden; + } + + .authorization { + float: right; + } + + .control-group { + margin-bottom: 15px; + } } } diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 0df2b330..c14795d7 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -127,36 +127,47 @@ en: wizard_field: "Wizard Field" user_field: "User Field" api: + label: "API" nav_label: 'APIs' new: 'New Api' - - auth: "Authentication" - auth_settings: "Settings" - auth_status: "Status" - service: 'Api Service' - redirect_uri: "Redirect Uri" - auth_type: 'Authorization Type' - auth_type_none: 'Select a type' - token_url: "Token Url" - auth_url: 'Authorization Url' - client_id: 'Client Id' - client_secret: 'Client Secret' - - status: "Status" - authorized: 'Authorized' - not_authorized: "Not Authorized" - params: 'Params' - param_new: 'New Param' - param_key: 'Param Key' - param_value: 'Param Value' - + name: "Name (can't be changed)" + name_placeholder: 'Underscored' + title: 'Title' + title_placeholder: 'Display name' remove: 'Delete' - authorize: 'Authorize' save: "Save" + auth: + label: "Authorization" + btn: 'Authorize' + settings: "Settings" + status: "Status" + redirect_uri: "Redirect url" + type: 'Type' + type_none: 'Select a type' + url: "Authorization url" + token_url: "Token url" + client_id: 'Client id' + client_secret: 'Client secret' + params: + label: 'Params' + new: 'New param' + key: 'key' + value: 'value' + + status: + label: "Status" + authorized: 'Authorized' + not_authorized: "Not authorized" + code: "Code" + access_token: "Access token" + refresh_token: "Refresh token" + expires_at: "Expires at" + refresh_at: "Refresh at" + endpoint: label: "Endpoints" - add: "Add Endpoint" + add: "Add endpoint" method: "Select a method" url: "Enter a url" diff --git a/controllers/api.rb b/controllers/api.rb index 059063ca..5d8ba61b 100644 --- a/controllers/api.rb +++ b/controllers/api.rb @@ -11,55 +11,75 @@ class CustomWizard::ApiController < ::ApplicationController CustomWizard::Api.list, each_serializer: CustomWizard::BasicApiSerializer ) - render json: MultiJson.dump(serializer) end def find - params.require(:service) - render_serialized(CustomWizard::Api.new(params[:service]), CustomWizard::ApiSerializer, root: false) + render_serialized(CustomWizard::Api.get(api_params[:name]), CustomWizard::ApiSerializer, root: false) end def save - byebug - params.require(:service) - service = params.permit(:service) - auth_data = params[:auth_params] - endpoints_data = params[:endpoints] + current = CustomWizard::Api.get(api_params[:name]) - service_auth_data = JSON.parse(auth_data) if !auth_data.nil? - service_endpoints = JSON.parse(endpoints_data) if !endpoints_data.nil? - - if !service_auth_data.nil? - CustomWizard::Api::Authorization.set(service, service_auth_data) + if api_params[:new] && current + raise Discourse::InvalidParameters, "An API with that name already exists: '#{current.title || current.name}'" end - if !service_endpoints.nil? - service_endpoints.each do |endpoint| - CustomWizard::Api::Endpoint.set(service, endpoint) + PluginStoreRow.transaction do + CustomWizard::Api.set(api_params[:name], title: api_params[:title]) + + if auth_data.present? + CustomWizard::Api::Authorization.set(api_params[:name], auth_data) + end + + if api_params[:endpoints].is_a? String + begin + endpoints = JSON.parse(api_params[:endpoints]) + endpoints.each do |endpoint| + CustomWizard::Api::Endpoint.set(api_params[:name], endpoint) + end + rescue => e + puts e + end end end render json: success_json.merge( - api: CustomWizard::ApiSerializer.new(params[:service], root: false) + api: CustomWizard::ApiSerializer.new( + CustomWizard::Api.new(api_params[:name]), + root: false + ) ) end + def remove + PluginStoreRow.transaction do + CustomWizard::Api.remove(api_params[:name]) + CustomWizard::Api::Authorization.remove(api_params[:name]) + CustomWizard::Api::Endpoint.remove(api_params[:name]) + end + + render json: success_json + end + def redirect - params.require(:service) - params.require(:code) + params.require(:name) + params.require(:code) - CustomWizard::Api::Authorization.set(params[:service], code: params[:code]) + CustomWizard::Api::Authorization.set(params[:name], code: params[:code]) + CustomWizard::Api::Authorization.get_token(params[:name]) - CustomWizard::Api::Authorization.get_token(params[:service]) - - return redirect_to path('/admin/wizards/apis/' + params[:service]) + return redirect_to path('/admin/wizards/apis/' + params[:name]) end private - def auth_data - @auth_data ||= params.permit( + def api_params + params.require(:name) + + data = params.permit( + :name, + :title, :auth_type, :auth_url, :token_url, @@ -67,11 +87,30 @@ class CustomWizard::ApiController < ::ApplicationController :client_secret, :username, :password, - :auth_params + :auth_params, + :endpoints, + :new ).to_h + + data[:name] = data[:name].underscore + + @api_params ||= data end - def endpoint_data - @endpoint_data ||= JSON.parse(params.permit(endpoints: [:id, :type, :url])) + def auth_data + auth_data = api_params.slice( + :auth_type, + :auth_url, + :token_url, + :client_id, + :client_secret, + :username, + :password, + :params + ) + + auth_data[:params] = JSON.parse(auth_data[:params]) if auth_data[:params].present? + + @auth_data ||= auth_data end end diff --git a/jobs/refresh_api_access_token.rb b/jobs/refresh_api_access_token.rb index c7fb94e5..edea0052 100644 --- a/jobs/refresh_api_access_token.rb +++ b/jobs/refresh_api_access_token.rb @@ -1,7 +1,7 @@ module Jobs class RefreshApiAccessToken < Jobs::Base def execute(args) - CustomWizard::Api::Authorization.refresh_token(args[:service]) + CustomWizard::Api::Authorization.refresh_token(args[:name]) end end end diff --git a/lib/api/api.rb b/lib/api/api.rb index dde491d4..2a614c6b 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -1,16 +1,32 @@ class CustomWizard::Api include ActiveModel::SerializerSupport - attr_accessor :service + attr_accessor :name, + :title - def initialize(service) - @service = service + def initialize(name, data={}) + @name = name + @title = data['title'] + end + + def self.set(name, data) + PluginStore.set("custom_wizard_api_#{name}", "metadata", data) + end + + def self.get(name) + if data = PluginStore.get("custom_wizard_api_#{name}", "metadata") + self.new(name, data) + end + end + + def self.remove(name) + PluginStore.remove("custom_wizard_api_#{name}", "metadata") end def self.list - PluginStoreRow.where("plugin_name LIKE 'custom_wizard_api_%' AND key = 'authorization'") + PluginStoreRow.where("plugin_name LIKE 'custom_wizard_api_%' AND key = 'metadata'") .map do |record| - self.new(record['plugin_name'].split('_').last) + self.new(record['plugin_name'].sub("custom_wizard_api_", ""), ::JSON.parse(record['value'])) end end end diff --git a/lib/api/authorization.rb b/lib/api/authorization.rb index 9ae5a211..35be314a 100644 --- a/lib/api/authorization.rb +++ b/lib/api/authorization.rb @@ -4,7 +4,7 @@ class CustomWizard::Api::Authorization include ActiveModel::SerializerSupport attr_accessor :authorized, - :service, + :name, :auth_type, :auth_url, :token_url, @@ -19,12 +19,15 @@ class CustomWizard::Api::Authorization :username, :password - def initialize(service, params) - @service = service - data = params.is_a?(String) ? ::JSON.parse(params) : params + def initialize(name, data, opts = {}) + unless opts[:data_only] + @name = name + end - data.each do |k, v| - self.send "#{k}=", v if self.respond_to?(k) + if data = data.is_a?(String) ? ::JSON.parse(data) : data + data.each do |k, v| + self.send "#{k}=", v if self.respond_to?(k) + end end end @@ -32,52 +35,56 @@ class CustomWizard::Api::Authorization @authorized ||= @access_token && @token_expires_at.to_datetime > Time.now end - def self.set(service, data) - model = self.get(service) || {} + def self.set(name, data = {}) + record = self.get(name, data_only: true) data.each do |k, v| - model.send "#{k}=", v if model.respond_to?(k) + record.send "#{k}=", v if record.respond_to?(k) end - PluginStore.set("custom_wizard_api_#{service}", 'authorization', model.as_json) + PluginStore.set("custom_wizard_api_#{name}", 'authorization', record.as_json) - self.get(service) + self.get(name) end - def self.get(service) - data = PluginStore.get("custom_wizard_api_#{service}", 'authorization') - self.new(service, data) + def self.get(name, opts = {}) + data = PluginStore.get("custom_wizard_api_#{name}", 'authorization') + self.new(name, data, opts) end - def self.get_header_authorization_string(service) - protocol = authentication_protocol(service) - raise Discourse::InvalidParameters.new(:service) unless protocol.present? + def self.remove(name) + PluginStore.remove("custom_wizard_api_#{name}", "authorization") + end + + def self.get_header_authorization_string(name) + protocol = authentication_protocol(name) + raise Discourse::InvalidParameters.new(:name) unless protocol.present? raise Discourse::InvalidParameters.new(:protocol) unless [BASIC_AUTH, OAUTH2_AUTH].include? protocol if protocol = BASIC_AUTH - username = username(service) + username = username(name) raise Discourse::InvalidParameters.new(:username) unless username.present? - password = password(service) + password = password(name) raise Discourse::InvalidParameters.new(:password) unless password.present? authorization_string = (username + ":" + password).chomp "Basic #{Base64.strict_encode64(authorization_string)}" else # must be OAUTH2 - access_token = access_token(service) + access_token = access_token(name) raise Discourse::InvalidParameters.new(access_token) unless access_token.present? "Bearer #{access_token}" end end - def self.get_token(service) - authorization = CustomWizard::Api::Authorization.get(service) + def self.get_token(name) + authorization = CustomWizard::Api::Authorization.get(name) body = { client_id: authorization.client_id, client_secret: authorization.client_secret, code: authorization.code, grant_type: 'authorization_code', - redirect_uri: Discourse.base_url + "/admin/wizards/apis/#{service}/redirect" + redirect_uri: Discourse.base_url + "/admin/wizards/apis/#{name}/redirect" } result = Excon.post( @@ -88,11 +95,11 @@ class CustomWizard::Api::Authorization :body => URI.encode_www_form(body) ) - self.handle_token_result(service, result) + self.handle_token_result(name, result) end - def self.refresh_token(service) - authorization = CustomWizard::Api::Authorization.get(service) + def self.refresh_token(name) + authorization = CustomWizard::Api::Authorization.get(name) body = { grant_type: 'refresh_token', @@ -110,10 +117,10 @@ class CustomWizard::Api::Authorization :body => URI.encode_www_form(body) ) - self.handle_token_result(service, result) + self.handle_token_result(name, result) end - def self.handle_token_result(service, result) + def self.handle_token_result(name, result) data = JSON.parse(result.body) return false if (data['error']) @@ -124,12 +131,12 @@ class CustomWizard::Api::Authorization refresh_at = expires_at.to_time - 2.hours opts = { - service: service + name: name } Jobs.enqueue_at(refresh_at, :refresh_api_access_token, opts) - CustomWizard::Api::Authorization.set(service, + CustomWizard::Api::Authorization.set(name, access_token: access_token, refresh_token: refresh_token, token_expires_at: expires_at, diff --git a/lib/api/endpoint.rb b/lib/api/endpoint.rb index b1e40e69..340a7e35 100644 --- a/lib/api/endpoint.rb +++ b/lib/api/endpoint.rb @@ -5,42 +5,46 @@ class CustomWizard::Api::Endpoint :method, :url - def initialize(service, params) - @service = service - data = params.is_a?(String) ? ::JSON.parse(params) : params - - data.each do |k, v| - self.send "#{k}=", v if self.respond_to?(k) + def initialize(name, params) + @name = name + if data = params.is_a?(String) ? ::JSON.parse(params) : params + data.each do |k, v| + self.send "#{k}=", v if self.respond_to?(k) + end end end - def self.set(service, data) - model = data[:endpoint_id] ? self.get(service, data[:endpoint_id]) : {} + def self.set(name, data) + model = data[:endpoint_id] ? self.get(name, data[:endpoint_id]) : {} endpoint_id = model[:endpoint_id] || SecureRandom.hex(8) data.each do |k, v| model.send "#{k}=", v if model.respond_to?(k) end - PluginStore.set("custom_wizard_api_#{service}", "endpoint_#{endpoint_id}", model.as_json) + PluginStore.set("custom_wizard_api_#{name}", "endpoint_#{endpoint_id}", model.as_json) - self.get(service) + self.get(name) end - def self.get(service, endpoint_id) + def self.get(name, endpoint_id) return nil if !endpoint_id - data = PluginStore.get("custom_wizard_api_#{service}", "endpoint_#{endpoint_id}") + data = PluginStore.get("custom_wizard_api_#{name}", "endpoint_#{endpoint_id}") data[:id] = endpoint_id - self.new(service, data) + self.new(name, data) + end + + def self.remove(name) + PluginStoreRow.where("plugin_name = 'custom_wizard_api_#{name}' AND key LIKE 'endpoint_%'").destroy_all end def self.list PluginStoreRow.where("plugin_name LIKE 'custom_wizard_api_%' AND key LIKE 'endpoint_%'") .map do |record| - service = record['plugin_name'].split('_').last + name = record['plugin_name'].sub("custom_wizard_api_", "") data = ::JSON.parse(record['value']) data[:id] = record['key'].split('_').last - self.new(service, data) + self.new(name, data) end end end diff --git a/plugin.rb b/plugin.rb index d35d929f..f0d7cfc6 100644 --- a/plugin.rb +++ b/plugin.rb @@ -69,9 +69,10 @@ after_initialize do get 'admin/wizards/submissions/:wizard_id' => 'admin#submissions' get 'admin/wizards/apis' => 'api#list' get 'admin/wizards/apis/new' => 'api#index' - get 'admin/wizards/apis/:service' => 'api#find' - put 'admin/wizards/apis/:service' => 'api#save' - get 'admin/wizards/apis/:service/redirect' => 'api#redirect' + get 'admin/wizards/apis/:name' => 'api#find' + put 'admin/wizards/apis/:name' => 'api#save' + delete 'admin/wizards/apis/:name' => 'api#remove' + get 'admin/wizards/apis/:name/redirect' => 'api#redirect' end end diff --git a/serializers/api/api_serializer.rb b/serializers/api/api_serializer.rb index aae825c2..0856b461 100644 --- a/serializers/api/api_serializer.rb +++ b/serializers/api/api_serializer.rb @@ -1,19 +1,24 @@ class CustomWizard::ApiSerializer < ApplicationSerializer - attributes :service, + attributes :name, + :title, :authorization, :endpoints def authorization - CustomWizard::Api::AuthorizationSerializer.new( - CustomWizard::Api::Authorization.get(object.service), - root: false - ) + if authorization = CustomWizard::Api::Authorization.get(object.name) + CustomWizard::Api::AuthorizationSerializer.new( + authorization, + root: false + ) + end end def endpoints - ActiveModel::ArraySerializer.new( - CustomWizard::Api::Endpoint.list, - each_serializer: CustomWizard::Api::EndpointSerializer - ) + if endpoints = CustomWizard::Api::Endpoint.list + ActiveModel::ArraySerializer.new( + endpoints, + each_serializer: CustomWizard::Api::EndpointSerializer + ) + end end end diff --git a/serializers/api/basic_api_serializer.rb b/serializers/api/basic_api_serializer.rb index b5430897..6f583f7d 100644 --- a/serializers/api/basic_api_serializer.rb +++ b/serializers/api/basic_api_serializer.rb @@ -1,3 +1,3 @@ class CustomWizard::BasicApiSerializer < ApplicationSerializer - attributes :service + attributes :name, :title end