Skip to content

Commit dbf858d

Browse files
authored
Merge pull request #6348 from ProcessMaker/task/FOUR-12942-develop
FOUR-14137 - Enable External Integration with BambooHR (develop)
2 parents 3476240 + 037abd5 commit dbf858d

10 files changed

+626
-165
lines changed

resources/js/admin/settings/components/AdditionalDriverConnectionProperties.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,17 @@ import ExcelConnectionProperties from "./cdata/ExcelConnectionProperties.vue";
1313
import GithubConnectionProperties from "./cdata/GithubConnectionProperties.vue";
1414
import DocusignConnectionProperties from "./cdata/DocusignConnectionProperties.vue";
1515
import GmailConnectionProperties from "./cdata/GmailConnectionProperties.vue";
16+
import BamboohrConnectionProperties from "./cdata/BamboohrConnectionProperties.vue";
17+
import SapHanaConnectionProperties from "./cdata/SapHanaConnectionProperties.vue";
1618
1719
export default {
1820
components: {
1921
ExcelConnectionProperties,
2022
GithubConnectionProperties,
2123
DocusignConnectionProperties,
2224
GmailConnectionProperties,
25+
BamboohrConnectionProperties,
26+
SapHanaConnectionProperties,
2327
},
2428
props: {
2529
formData: {
@@ -38,6 +42,8 @@ export default {
3842
"cdata.github": "github-connection-properties",
3943
"cdata.docusign": "docusign-connection-properties",
4044
"cdata.gmail": "gmail-connection-properties",
45+
"cdata.BambooHR": "bamboohr-connection-properties",
46+
"cdata.saphana": "sap-hana-connection-properties",
4147
},
4248
};
4349
},

resources/js/admin/settings/components/SettingDriverAuthorization.vue

Lines changed: 80 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
<template>
22
<div>
3+
<!-- Authorize badge -->
34
<div v-if="hasAuthorizedBadge">
45
<b-badge
56
pill
67
:variant="isAuthorized ? 'success' : 'warning'"
78
>
8-
<span v-if="isAuthorized">{{ $t('Authorized') }}</span>
9-
<span v-else>{{ $t('Not Authorized') }}</span>
9+
<span v-if="isAuthorized">{{ $t("Authorized") }}</span>
10+
<span v-else>{{ $t("Not Authorized") }}</span>
1011
</b-badge>
1112
</div>
1213
<div v-else>
13-
Empty
14+
{{ $t("Empty") }}
1415
</div>
16+
17+
<!-- Connection properties modal -->
1518
<b-modal
1619
v-model="showModal"
1720
class="setting-object-modal"
@@ -24,110 +27,30 @@
2427
class="d-block"
2528
>
2629
<div>
27-
<h5
28-
v-if="setting.name"
29-
class="mb-0"
30-
>
31-
{{ $t(setting.name) }}
32-
</h5>
33-
<h5
34-
v-else
35-
class="mb-0"
36-
>
37-
{{ setting.key }}
30+
<h5 class="mb-0">
31+
<span v-if="setting.name">{{ $t(setting.name) }}</span>
32+
<span v-else>{{ $t(setting.key) }}</span>
3833
</h5>
39-
<small class="form-text text-muted">{{ $t('Configure the driver connection properties.') }}</small>
34+
<small class="form-text text-muted">
35+
{{ $t("Configure the driver connection properties.") }}
36+
</small>
4037
</div>
4138
<button
4239
type="button"
43-
:aria-label="$t('Close')"
4440
class="close"
41+
:aria-label="$t('Close')"
4542
@click="onCancel"
4643
>
47-
×
44+
&times;
4845
</button>
4946
</template>
5047
<div>
51-
<b-form-group
52-
required
53-
:label="$t('Client ID')"
54-
:description="formDescription('The client ID assigned when you register your application.', 'client_id', errors)"
55-
:invalid-feedback="errorMessage('client_id', errors)"
56-
:state="errorState('client_id', errors)"
57-
>
58-
<b-form-input
59-
v-model="formData.client_id"
60-
required
61-
autofocus
62-
autocomplete="off"
63-
:state="errorState('client_id', errors)"
64-
name="client_id"
65-
data-cy="client_id"
66-
/>
67-
</b-form-group>
68-
69-
<b-form-group
70-
required
71-
:label="$t('Client Secret')"
72-
:description="formDescription('The client secret assigned when you register your application.', 'client_secret', errors)"
73-
:invalid-feedback="errorMessage('client_secret', errors)"
74-
:state="errorState('client_secret', errors)"
75-
>
76-
<b-input-group>
77-
<b-form-input
78-
v-model="formData.client_secret"
79-
required
80-
autofocus
81-
autocomplete="off"
82-
trim
83-
:type="type"
84-
:state="errorState('client_secret', errors)"
85-
name="client_secret"
86-
data-cy="client_secret"
87-
/>
88-
<b-input-group-append>
89-
<b-button
90-
:aria-label="$t('Toggle Show Password')"
91-
variant="secondary"
92-
@click="togglePassword"
93-
>
94-
<i
95-
class="fas"
96-
:class="icon"
97-
/>
98-
</b-button>
99-
</b-input-group-append>
100-
</b-input-group>
101-
</b-form-group>
102-
103-
<b-form-group
104-
required
105-
:label="$t('Redirect URL')"
106-
:description="formDescription('This value must match the callback URL you specify in your app settings.', 'callback_url', errors)"
107-
:invalid-feedback="errorMessage('callback_url', errors)"
108-
:state="errorState('callback_url', errors)"
109-
>
110-
<b-input-group>
111-
<b-form-input
112-
v-model="formData.callback_url"
113-
autofocus
114-
readonly
115-
autocomplete="off"
116-
:state="errorState('callback_url', errors)"
117-
name="callback_url"
118-
data-cy="callback_url"
119-
/>
120-
<b-input-group-append>
121-
<b-button
122-
:aria-label="$t('Copy')"
123-
variant="secondary"
124-
@click="onCopy"
125-
>
126-
<i class="fas fa-copy" />
127-
</b-button>
128-
</b-input-group-append>
129-
</b-input-group>
130-
</b-form-group>
48+
<component
49+
:is="authSchemeToComponent(setting.config?.AuthScheme)"
50+
:form-data="formData"
51+
:auth-scheme="setting.config?.AuthScheme"
52+
@updateFormData="updateFormData"
53+
/>
13154

13255
<additional-driver-connection-properties
13356
:driver-key="setting?.key"
@@ -145,20 +68,20 @@
14568
data-cy="cancel-button"
14669
@click="onCancel"
14770
>
148-
{{ $t('Cancel') }}
71+
{{ $t("Cancel") }}
14972
</button>
15073
<button
15174
type="button"
15275
class="btn btn-secondary ml-3"
15376
data-cy="authorize-button"
154-
:disabled="isButtonDisabled"
15577
@click="onSave"
15678
>
157-
{{ $t('Authorize') }}
79+
{{ $t("Authorize") }}
15880
</button>
15981
</div>
16082
</b-modal>
16183

84+
<!-- Authorizing modal -->
16285
<b-modal
16386
v-model="showAuthorizingModal"
16487
class="setting-object-modal"
@@ -169,26 +92,33 @@
16992
no-fade
17093
>
17194
<div class="text-center">
172-
<h3>{{ $t('Connecting Driver') }}</h3>
95+
<h3>{{ $t("Connecting Driver") }}</h3>
17396
<i class="fas fa-circle-notch fa-spin fa-3x p-0 text-primary" />
17497
</div>
17598
</b-modal>
17699
</div>
177100
</template>
178-
179101
<script>
180102
// eslint-disable-next-line import/no-unresolved
181103
import { FormErrorsMixin, Required } from "SharedComponents";
182104
import settingMixin from "../mixins/setting";
183105
import AdditionalDriverConnectionProperties from "./AdditionalDriverConnectionProperties.vue";
106+
import OauthConnectionProperties from "./cdata/OauthConnectionProperties.vue";
107+
import NoneConnectionProperties from "./cdata/NoneConnectionProperties.vue";
108+
import PasswordConnectionProperties from "./cdata/PasswordConnectionProperties.vue";
184109
185110
export default {
186-
components: { AdditionalDriverConnectionProperties },
111+
components: {
112+
AdditionalDriverConnectionProperties,
113+
OauthConnectionProperties,
114+
NoneConnectionProperties,
115+
PasswordConnectionProperties,
116+
},
187117
mixins: [settingMixin, FormErrorsMixin, Required],
188118
props: {
189119
setting: {
190120
type: [Object, null],
191-
default: null,
121+
default: () => ({}),
192122
},
193123
value: {
194124
type: Object,
@@ -198,19 +128,19 @@ export default {
198128
data() {
199129
return {
200130
input: "",
201-
formData: {
202-
client_id: "",
203-
client_secret: "",
204-
callback_url: "",
205-
},
131+
formData: {},
206132
selected: null,
207133
showModal: false,
208134
showAuthorizingModal: false,
209135
transformed: null,
210136
errors: {},
211-
isInvalid: true,
212-
type: "password",
213137
resetData: true,
138+
componentsMap: {
139+
OAuth: "oauth-connection-properties",
140+
None: "none-connection-properties",
141+
Password: "password-connection-properties",
142+
Basic: "password-connection-properties",
143+
},
214144
};
215145
},
216146
computed: {
@@ -227,57 +157,18 @@ export default {
227157
}
228158
return false;
229159
},
230-
changed() {
231-
return JSON.stringify(this.formData) !== JSON.stringify(this.transformed);
232-
},
233-
icon() {
234-
if (this.type === "password") {
235-
return "fa-eye";
236-
}
237-
return "fa-eye-slash";
238-
},
239-
isButtonDisabled() {
240-
return this.isInvalid || (this.isAuthorized && !this.changed);
241-
},
242-
},
243-
watch: {
244-
formData: {
245-
handler() {
246-
this.isInvalid = this.validateData();
247-
},
248-
deep: true,
249-
},
250160
},
251161
mounted() {
252162
if (this.value === null) {
253163
this.resetFormData();
254164
} else {
255165
this.formData = this.value;
256166
}
257-
this.isInvalid = this.validateData();
258167
this.transformed = this.copy(this.formData);
259168
},
260169
methods: {
261-
onCopy() {
262-
navigator.clipboard.writeText(this.formData.callback_url).then(() => {
263-
ProcessMaker.alert(this.$t("The setting was copied to your clipboard."), "success");
264-
}, () => {
265-
ProcessMaker.alert(this.$t("The setting was not copied to your clipboard."), "danger");
266-
});
267-
},
268-
togglePassword() {
269-
if (this.type === "text") {
270-
this.type = "password";
271-
} else {
272-
this.type = "text";
273-
}
274-
},
275-
validateData() {
276-
// Check if client_id and client_secret are empty
277-
const clientIdEmpty = _.isEmpty(this.formData.client_id);
278-
const clientSecretEmpty = _.isEmpty(this.formData.client_secret);
279-
280-
return _.isEmpty(this.formData) || clientIdEmpty || clientSecretEmpty;
170+
authSchemeToComponent(scheme) {
171+
return this.componentsMap[scheme] || null;
281172
},
282173
onCancel() {
283174
this.showModal = false;
@@ -294,14 +185,48 @@ export default {
294185
onModalHidden() {
295186
this.resetFormData();
296187
},
188+
generateCallbackUrl(item) {
189+
if (item.config.AuthScheme === "OAuth") {
190+
const name = item.key.split("cdata.")[1];
191+
const appUrl = document.head.querySelector("meta[name=\"app-url\"]").content;
192+
193+
this.formData.callback_url = `${appUrl}/external-integrations/${name}`;
194+
}
195+
},
297196
authorizeConnection() {
298197
this.showAuthorizingModal = true;
299198
this.showModal = false;
300199
this.resetData = false;
301-
ProcessMaker.apiClient.post(`settings/${this.setting.id}/get-oauth-url`, this.formData)
200+
201+
if (this.setting.config.AuthScheme === "OAuth") {
202+
this.authorizeOAuthConnection();
203+
} else {
204+
this.authorizeNoneConnection();
205+
}
206+
},
207+
authorizeOAuthConnection() {
208+
ProcessMaker.apiClient
209+
.post(`settings/${this.setting.id}/get-oauth-url`, this.formData)
302210
.then((response) => {
303211
window.location = response.data?.url;
304212
})
213+
.catch((error) => {
214+
const errorMessage = error.response?.data?.message || error.message;
215+
ProcessMaker.alert(errorMessage, "danger");
216+
})
217+
.finally(() => {
218+
this.showModal = true;
219+
this.showAuthorizingModal = false;
220+
});
221+
},
222+
authorizeNoneConnection() {
223+
ProcessMaker.apiClient
224+
.post(`settings/${this.setting.id}/authorize-driver`, this.formData)
225+
.then((response) => {
226+
window.location = response.data.url;
227+
this.showModal = false;
228+
this.showAuthorizingModal = true;
229+
})
305230
.catch((error) => {
306231
const errorMessage = error.response?.data?.message || error.message;
307232
ProcessMaker.alert(errorMessage, "danger");
@@ -310,18 +235,11 @@ export default {
310235
});
311236
},
312237
onSave() {
313-
const driver = this.setting.key.split("cdata.")[1];
314-
315-
this.formData.driver = driver;
238+
this.formData.name = this.setting.config?.name;
239+
this.formData.driver = this.setting.config?.driver;
316240
this.transformed = { ...this.formData };
317241
this.authorizeConnection();
318242
},
319-
generateCallbackUrl(data) {
320-
const name = data.key.split("cdata.")[1];
321-
const appUrl = document.head.querySelector("meta[name=\"app-url\"]").content;
322-
323-
this.formData.callback_url = `${appUrl}/external-integrations/${name}`;
324-
},
325243
resetFormData() {
326244
if (this.resetData) {
327245
this.formData = {

0 commit comments

Comments
 (0)