1
0
Fork 0

Add custom wizard integration

Dieser Commit ist enthalten in:
Angus McLeod 2019-06-03 17:09:24 +10:00
Ursprung 819c76b3ff
Commit 5ffcee1dde
14 geänderte Dateien mit 175 neuen und 30 gelöschten Zeilen

Datei anzeigen

@ -3,7 +3,8 @@ import { default as computed, observes } from 'ember-addons/ember-computed-decor
const ACTION_TYPES = [
{ id: 'create_topic', name: 'Create Topic' },
{ id: 'update_profile', name: 'Update Profile' },
{ id: 'send_message', name: 'Send Message' }
{ id: 'send_message', name: 'Send Message' },
{ id: 'send_to_api', name: 'Send to API' }
];
const PROFILE_FIELDS = [
@ -26,6 +27,8 @@ export default Ember.Component.extend({
createTopic: Ember.computed.equal('action.type', 'create_topic'),
updateProfile: Ember.computed.equal('action.type', 'update_profile'),
sendMessage: Ember.computed.equal('action.type', 'send_message'),
sendToApi: Ember.computed.equal('action.type', 'send_to_api'),
apiEmpty: Ember.computed.empty('action.api'),
disableId: Ember.computed.not('action.isNew'),
@computed('currentStepId', 'wizard.save_submissions')
@ -75,5 +78,21 @@ export default Ember.Component.extend({
toggleCustomCategoryWizardField() {
const user = this.get('action.custom_category_user_field');
if (user) this.set('action.custom_category_wizard_field', false);
},
@computed('wizard.apis')
availableApis(apis) {
return apis.map(a => {
return {
id: a.name,
name: a.title
};
});
},
@computed('wizard.apis', 'action.api')
availableEndpoints(apis, api) {
if (!api) return [];
return apis.find(a => a.name === api).endpoints;
}
});

Datei anzeigen

@ -78,7 +78,6 @@ export default Ember.Controller.extend({
if (api.title) data['title'] = api.title;
const originalTitle = this.get('api.originalTitle');
console.log(api, originalTitle);
if (api.get('isNew') || (originalTitle && (api.title !== originalTitle))) {
refreshList = true;
}

Datei anzeigen

@ -33,7 +33,8 @@ export default Discourse.Route.extend({
afterModel(model) {
return Ember.RSVP.all([
this._getFieldTypes(model),
this._getThemes(model)
this._getThemes(model),
this._getApis(model)
]);
},
@ -48,6 +49,11 @@ export default Discourse.Route.extend({
});
},
_getApis(model) {
return ajax('/admin/wizards/apis')
.then((result) => model.set('apis', result));
},
setupController(controller, model) {
const newWizard = this.get('newWizard');
const steps = model.get('steps') || [];

Datei anzeigen

@ -96,7 +96,7 @@
<label>{{i18n 'admin.wizard.action.post_builder.user_fields'}}{{builderUserFields}}</label>
<label>{{i18n 'admin.wizard.action.post_builder.wizard_fields'}}{{builderWizardFields}}</label>
{{d-editor value=action.post_template
placeholder='admin.wizard.action.post_builder.placeholder'
placeholder='admin.wizard.action.interpolate_fields'
classNames='post-builder-editor'}}
</div>
</div>
@ -157,7 +157,7 @@
<label>{{i18n 'admin.wizard.action.post_builder.user_fields'}}{{builderUserFields}}</label>
<label>{{i18n 'admin.wizard.action.post_builder.wizard_fields'}}{{builderWizardFields}}</label>
{{d-editor value=action.post_template
placeholder='admin.wizard.action.post_builder.placeholder'
placeholder='admin.wizard.action.interpolate_fields'
classNames='post-builder-editor'}}
</div>
</div>
@ -204,3 +204,41 @@
allowUserField=true}}
</div>
{{/if}}
{{#if sendToApi}}
<div class="setting">
<div class="setting-label">
<h3>{{i18n "admin.wizard.action.send_to_api.api"}}</h3>
</div>
<div class="setting-value">
{{combo-box value=action.api
content=availableApis
none='admin.wizard.action.send_to_api.select_an_api'
isDisabled=action.custom_title_enabled}}
</div>
</div>
<div class="setting">
<div class="setting-label">
<h3>{{i18n "admin.wizard.action.send_to_api.endpoint"}}</h3>
</div>
<div class="setting-value">
{{combo-box value=action.api_endpoint
content=availableEndpoints
none='admin.wizard.action.send_to_api.select_an_endpoint'
isDisabled=apiEmpty}}
</div>
</div>
<div class="setting api-body">
<div class="setting-label">
<h3>{{i18n "admin.wizard.action.send_to_api.body"}}</h3>
</div>
<div class="setting-value">
<label>{{i18n 'admin.wizard.action.post_builder.user_fields'}}{{builderUserFields}}</label>
<label>{{i18n 'admin.wizard.action.post_builder.wizard_fields'}}{{builderWizardFields}}</label>
{{textarea value=action.api_body
placeholder=(i18n 'admin.wizard.action.interpolate_fields')}}
</div>
</div>
{{/if}}

Datei anzeigen

@ -117,6 +117,23 @@
margin-top: 5px;
padding: 5px;
}
.api-body {
width: 100%;
.setting-label {
max-width: 70px;
}
.setting-value {
width: calc(100% - 180px);
}
textarea {
width: 100%;
min-height: 150px;
}
}
}
.wizard-links {

Datei anzeigen

@ -103,6 +103,8 @@ en:
add_fields: "{{type}} Fields"
available_fields: "* If 'Save wizard submissions' is disabled, only the fields of the current step are available to the current step's actions."
topic_attr: "Topic Attribute"
interpolate_fields: "Insert wizard fields using the field_id in w{}. Insert user fields using field key in u{}."
skip_redirect:
label: "Skip Redirect"
description: "Don't redirect the user to this {{type}} after the wizard completes"
@ -120,12 +122,19 @@ en:
label: "Builder"
user_fields: "User Fields: "
wizard_fields: "Wizard Fields: "
placeholder: "Insert wizard fields using the field_id in w{}. Insert user fields using field key in u{}."
custom_title: "Custom Title"
custom_category:
label: "Custom Category"
wizard_field: "Wizard Field"
user_field: "User Field"
send_to_api:
label: "Send to API"
api: "API"
endpoint: "Endpoint"
select_an_api: "Select an API"
select_an_endpoint: "Select an endpoint"
body: "Request body JSON"
api:
label: "API"
nav_label: 'APIs'

Datei anzeigen

@ -106,10 +106,10 @@ class CustomWizard::ApiController < ::ApplicationController
:client_secret,
:username,
:password,
:params
:auth_params
)
auth_data[:params] = JSON.parse(auth_data[:params]) if auth_data[:params].present?
auth_data[:auth_params] = JSON.parse(auth_data[:auth_params]) if auth_data[:auth_params].present?
@auth_data ||= auth_data
end

Datei anzeigen

@ -32,6 +32,8 @@ class CustomWizard::Api::Authorization
end
def self.set(api_name, new_data = {})
api_name = api_name.underscore
data = self.get(api_name, data_only: true) || {}
new_data.each do |k, v|
@ -44,6 +46,8 @@ class CustomWizard::Api::Authorization
end
def self.get(api_name, opts = {})
api_name = api_name.underscore
if data = PluginStore.get("custom_wizard_api_#{api_name}", 'authorization')
if opts[:data_only]
data
@ -60,22 +64,16 @@ class CustomWizard::Api::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
auth = CustomWizard::Api::Authorization.get(name)
raise Discourse::InvalidParameters.new(:name) unless auth.present?
if protocol = BASIC_AUTH
username = username(name)
raise Discourse::InvalidParameters.new(:username) unless username.present?
password = password(name)
raise Discourse::InvalidParameters.new(:password) unless password.present?
authorization_string = (username + ":" + password).chomp
"Basic #{Base64.strict_encode64(authorization_string)}"
if auth.auth_type === "basic"
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
# must be OAUTH2
access_token = access_token(name)
raise Discourse::InvalidParameters.new(access_token) unless access_token.present?
"Bearer #{access_token}"
raise Discourse::InvalidParameters.new(auth.access_token) unless auth.access_token.present?
"Bearer #{auth.access_token}"
end
end

Datei anzeigen

@ -16,8 +16,13 @@ class CustomWizard::Api::Endpoint
end
def self.set(api_name, new_data)
data = new_data[:endpoint_id] ? self.get(api_name, new_data[:endpoint_id], data_only: true) : {}
endpoint_id = new_data[:endpoint_id] || SecureRandom.hex(3)
if new_data['id']
data = self.get(api_name, new_data['id'], data_only: true)
endpoint_id = new_data['id']
else
data = {}
endpoint_id = SecureRandom.hex(3)
end
new_data.each do |k, v|
data[k.to_sym] = v
@ -32,11 +37,10 @@ class CustomWizard::Api::Endpoint
return nil if !endpoint_id
if data = PluginStore.get("custom_wizard_api_#{api_name}", "endpoint_#{endpoint_id}")
data[:id] = endpoint_id
if opts[:data_only]
data
else
data[:id] = endpoint_id
self.new(api_name, data)
end
else
@ -48,8 +52,8 @@ class CustomWizard::Api::Endpoint
PluginStoreRow.where("plugin_name = 'custom_wizard_api_#{api_name}' AND key LIKE 'endpoint_%'").destroy_all
end
def self.list
PluginStoreRow.where("plugin_name LIKE 'custom_wizard_api_%' AND key LIKE 'endpoint_%'")
def self.list(api_name)
PluginStoreRow.where("plugin_name LIKE 'custom_wizard_api_#{api_name}' AND key LIKE 'endpoint_%'")
.map do |record|
api_name = record['plugin_name'].sub("custom_wizard_api_", "")
data = ::JSON.parse(record['value'])
@ -57,4 +61,32 @@ class CustomWizard::Api::Endpoint
self.new(api_name, data)
end
end
def self.request(api_name, endpoint_id, body)
endpoint = self.get(api_name, endpoint_id)
auth = CustomWizard::Api::Authorization.get_header_authorization_string(api_name)
connection = Excon.new(
URI.parse(URI.encode(endpoint.url)).to_s,
:headers => {
"Authorization" => auth,
"Accept" => "application/json, */*",
"Content-Type" => "application/json"
}
)
params = {
method: endpoint.method
}
if body
body = JSON.generate(body)
body.delete! '\\'
params[:body] = body
end
response = connection.request(params)
JSON.parse(response.body)
end
end

Datei anzeigen

@ -393,6 +393,17 @@ class CustomWizard::Builder
end
end
def send_to_api(user, action, data)
api_body = CustomWizard::Builder.fill_placeholders(JSON.generate(JSON.parse(action['api_body'])), user, data)
result = CustomWizard::Api::Endpoint.request(action['api'], action['api_endpoint'], api_body)
if result['error']
updater.errors.add(:send_message, result['error'])
else
## add validation callback
end
end
def save_submissions(data, final_step)
if final_step
data['submitted_at'] = Time.now.iso8601

Datei anzeigen

@ -97,6 +97,7 @@ after_initialize do
load File.expand_path('../serializers/api/authorization_serializer.rb', __FILE__)
load File.expand_path('../serializers/api/basic_api_serializer.rb', __FILE__)
load File.expand_path('../serializers/api/endpoint_serializer.rb', __FILE__)
load File.expand_path('../serializers/api/basic_endpoint_serializer.rb', __FILE__)
::UsersController.class_eval do
def wizard_path

Datei anzeigen

@ -14,7 +14,7 @@ class CustomWizard::ApiSerializer < ApplicationSerializer
end
def endpoints
if endpoints = CustomWizard::Api::Endpoint.list
if endpoints = CustomWizard::Api::Endpoint.list(object.name)
ActiveModel::ArraySerializer.new(
endpoints,
each_serializer: CustomWizard::Api::EndpointSerializer

Datei anzeigen

@ -1,3 +1,14 @@
class CustomWizard::BasicApiSerializer < ApplicationSerializer
attributes :name, :title
attributes :name,
:title,
:endpoints
def endpoints
if endpoints = CustomWizard::Api::Endpoint.list(object.name)
ActiveModel::ArraySerializer.new(
endpoints,
each_serializer: CustomWizard::Api::BasicEndpointSerializer
)
end
end
end

Datei anzeigen

@ -0,0 +1,4 @@
class CustomWizard::Api::BasicEndpointSerializer < ApplicationSerializer
attributes :id,
:name
end