Compare commits
52 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
daa38ab846 | ||
![]() |
00e18cf42d | ||
![]() |
b3d2a1167c | ||
![]() |
43c34fcb7b | ||
![]() |
c98f24338f | ||
![]() |
d0748bb7e2 | ||
![]() |
81e8e4963c | ||
![]() |
282b2374a2 | ||
![]() |
b7e38f9d1c | ||
![]() |
024c7476c7 | ||
![]() |
30a7ffe93d | ||
![]() |
e2d803ebf7 | ||
![]() |
be7e67c940 | ||
![]() |
ead4b13ba5 | ||
![]() |
e02c42ee18 | ||
![]() |
d39886fdf8 | ||
![]() |
11a425f1de | ||
![]() |
f0e194e584 | ||
![]() |
d4b9331cf2 | ||
![]() |
37e1acc5f1 | ||
![]() |
ffaf6a577d | ||
![]() |
afdaf6ba39 | ||
![]() |
4c49367910 | ||
![]() |
a506c4411d | ||
![]() |
1859c9854e | ||
![]() |
6ff29b9ae6 | ||
![]() |
3578f6b849 | ||
![]() |
0347864fde | ||
![]() |
5f9786a2c7 | ||
![]() |
75aeff1898 | ||
![]() |
0afcdce6d3 | ||
![]() |
a591d0ea87 | ||
![]() |
0e111a3532 | ||
![]() |
b599466ffa | ||
![]() |
69727e78df | ||
![]() |
02ae67b147 | ||
![]() |
a769f78801 | ||
![]() |
d583e42428 | ||
![]() |
da732becb6 | ||
![]() |
b89a4d58d9 | ||
![]() |
09854147d1 | ||
![]() |
3648c2bfe3 | ||
![]() |
3f3ee032f6 | ||
![]() |
68e5d54331 | ||
![]() |
824c434b0b | ||
![]() |
9f0e0ca656 | ||
![]() |
95f89ba03e | ||
![]() |
697f72ecf4 | ||
![]() |
4f03f2ab51 | ||
![]() |
c81531cb7a | ||
![]() |
7b6e4aa153 | ||
![]() |
f21039d19d |
@@ -8,7 +8,7 @@
|
|||||||
"version": "latest"
|
"version": "latest"
|
||||||
},
|
},
|
||||||
"ghcr.io/devcontainers/features/node:1": {
|
"ghcr.io/devcontainers/features/node:1": {
|
||||||
"version": 20
|
"version": 18
|
||||||
},
|
},
|
||||||
"ghcr.io/devcontainers/features/common-utils:1": {
|
"ghcr.io/devcontainers/features/common-utils:1": {
|
||||||
"username": "vscode",
|
"username": "vscode",
|
||||||
|
@@ -33,7 +33,6 @@
|
|||||||
"axios": "1.6.0",
|
"axios": "1.6.0",
|
||||||
"bcrypt": "^5.0.1",
|
"bcrypt": "^5.0.1",
|
||||||
"bullmq": "^3.0.0",
|
"bullmq": "^3.0.0",
|
||||||
"copyfiles": "^2.4.1",
|
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"crypto-js": "^4.1.1",
|
"crypto-js": "^4.1.1",
|
||||||
"debug": "~2.6.9",
|
"debug": "~2.6.9",
|
||||||
@@ -45,7 +44,6 @@
|
|||||||
"graphql-middleware": "^6.1.15",
|
"graphql-middleware": "^6.1.15",
|
||||||
"graphql-shield": "^7.5.0",
|
"graphql-shield": "^7.5.0",
|
||||||
"graphql-tools": "^8.2.0",
|
"graphql-tools": "^8.2.0",
|
||||||
"graphql-type-json": "^0.3.2",
|
|
||||||
"handlebars": "^4.7.7",
|
"handlebars": "^4.7.7",
|
||||||
"http-errors": "~1.6.3",
|
"http-errors": "~1.6.3",
|
||||||
"http-proxy-agent": "^7.0.0",
|
"http-proxy-agent": "^7.0.0",
|
||||||
@@ -68,7 +66,6 @@
|
|||||||
"pluralize": "^8.0.0",
|
"pluralize": "^8.0.0",
|
||||||
"raw-body": "^2.5.2",
|
"raw-body": "^2.5.2",
|
||||||
"showdown": "^2.1.0",
|
"showdown": "^2.1.0",
|
||||||
"stripe": "^11.13.0",
|
|
||||||
"winston": "^3.7.1",
|
"winston": "^3.7.1",
|
||||||
"xmlrpc": "^1.3.2"
|
"xmlrpc": "^1.3.2"
|
||||||
},
|
},
|
||||||
|
@@ -0,0 +1,161 @@
|
|||||||
|
import defineAction from '../../../../helpers/define-action.js';
|
||||||
|
|
||||||
|
export default defineAction({
|
||||||
|
name: 'Create company',
|
||||||
|
key: 'createCompany',
|
||||||
|
description: 'Creates a new company.',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
label: 'Company Owner',
|
||||||
|
key: 'companyOwnerId',
|
||||||
|
type: 'dropdown',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
source: {
|
||||||
|
type: 'query',
|
||||||
|
name: 'getDynamicData',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: 'key',
|
||||||
|
value: 'listContactOwners',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Company Name',
|
||||||
|
key: 'companyName',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Phone',
|
||||||
|
key: 'phone',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Website',
|
||||||
|
key: 'website',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Tags',
|
||||||
|
key: 'tags',
|
||||||
|
type: 'dynamic',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
label: 'Tag',
|
||||||
|
key: 'tag',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Description',
|
||||||
|
key: 'description',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Billing Street',
|
||||||
|
key: 'billingStreet',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Billing City',
|
||||||
|
key: 'billingCity',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Billing State',
|
||||||
|
key: 'billingState',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Billing Country',
|
||||||
|
key: 'billingCountry',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Billing Code',
|
||||||
|
key: 'billingCode',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const {
|
||||||
|
contactOwnerId,
|
||||||
|
companyName,
|
||||||
|
phone,
|
||||||
|
website,
|
||||||
|
tags,
|
||||||
|
description,
|
||||||
|
billingStreet,
|
||||||
|
billingCity,
|
||||||
|
billingState,
|
||||||
|
billingCountry,
|
||||||
|
billingCode,
|
||||||
|
} = $.step.parameters;
|
||||||
|
|
||||||
|
const allTags = tags.map((tag) => ({
|
||||||
|
name: tag.tag,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const body = {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
Owner: {
|
||||||
|
id: contactOwnerId,
|
||||||
|
},
|
||||||
|
Account_Name: companyName,
|
||||||
|
Phone: phone,
|
||||||
|
Website: website,
|
||||||
|
Tag: allTags,
|
||||||
|
Description: description,
|
||||||
|
Billing_Street: billingStreet,
|
||||||
|
Billing_City: billingCity,
|
||||||
|
Billing_State: billingState,
|
||||||
|
Billing_Country: billingCountry,
|
||||||
|
Billing_Code: billingCode,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data } = await $.http.post(`/bigin/v2/Accounts`, body);
|
||||||
|
|
||||||
|
$.setActionItem({
|
||||||
|
raw: data[0],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
@@ -0,0 +1,217 @@
|
|||||||
|
import defineAction from '../../../../helpers/define-action.js';
|
||||||
|
|
||||||
|
export default defineAction({
|
||||||
|
name: 'Create contact',
|
||||||
|
key: 'createContact',
|
||||||
|
description: 'Creates a new contact.',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
label: 'Contact Owner',
|
||||||
|
key: 'contactOwnerId',
|
||||||
|
type: 'dropdown',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
source: {
|
||||||
|
type: 'query',
|
||||||
|
name: 'getDynamicData',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: 'key',
|
||||||
|
value: 'listContactOwners',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'First Name',
|
||||||
|
key: 'firstName',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Last Name',
|
||||||
|
key: 'lastName',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Title',
|
||||||
|
key: 'title',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Email',
|
||||||
|
key: 'email',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Company ID',
|
||||||
|
key: 'companyId',
|
||||||
|
type: 'dropdown',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
source: {
|
||||||
|
type: 'query',
|
||||||
|
name: 'getDynamicData',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: 'key',
|
||||||
|
value: 'listCompanies',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Mobile',
|
||||||
|
key: 'mobile',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Email Opt Out',
|
||||||
|
key: 'emailOptOut',
|
||||||
|
type: 'dropdown',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
options: [
|
||||||
|
{ label: 'True', value: true },
|
||||||
|
{ label: 'False', value: false },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Tags',
|
||||||
|
key: 'tags',
|
||||||
|
type: 'dynamic',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
label: 'Tag',
|
||||||
|
key: 'tag',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Description',
|
||||||
|
key: 'description',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Mailing Street',
|
||||||
|
key: 'mailingStreet',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Mailing City',
|
||||||
|
key: 'mailingCity',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Mailing State',
|
||||||
|
key: 'mailingState',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Mailing Country',
|
||||||
|
key: 'mailingCountry',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Mailing Zip',
|
||||||
|
key: 'mailingZip',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const {
|
||||||
|
contactOwnerId,
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
title,
|
||||||
|
email,
|
||||||
|
companyId,
|
||||||
|
mobile,
|
||||||
|
emailOptOut,
|
||||||
|
tags,
|
||||||
|
description,
|
||||||
|
mailingStreet,
|
||||||
|
mailingCity,
|
||||||
|
mailingState,
|
||||||
|
mailingCountry,
|
||||||
|
mailingZip,
|
||||||
|
} = $.step.parameters;
|
||||||
|
|
||||||
|
const allTags = tags.map((tag) => ({
|
||||||
|
name: tag.tag,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const body = {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
Owner: {
|
||||||
|
id: contactOwnerId,
|
||||||
|
},
|
||||||
|
Account_Name: {
|
||||||
|
id: companyId,
|
||||||
|
},
|
||||||
|
First_Name: firstName,
|
||||||
|
Last_Name: lastName,
|
||||||
|
Title: title,
|
||||||
|
Email: email,
|
||||||
|
Mobile: mobile,
|
||||||
|
Email_Opt_Out: emailOptOut,
|
||||||
|
Tag: allTags,
|
||||||
|
Description: description,
|
||||||
|
Mailing_Street: mailingStreet,
|
||||||
|
Mailing_City: mailingCity,
|
||||||
|
Mailing_State: mailingState,
|
||||||
|
Mailing_Country: mailingCountry,
|
||||||
|
Mailing_Zip: mailingZip,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data } = await $.http.post(`/bigin/v2/Contacts`, body);
|
||||||
|
|
||||||
|
$.setActionItem({
|
||||||
|
raw: data[0],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
@@ -0,0 +1,293 @@
|
|||||||
|
import defineAction from '../../../../helpers/define-action.js';
|
||||||
|
|
||||||
|
export default defineAction({
|
||||||
|
name: 'Create event',
|
||||||
|
key: 'createEvent',
|
||||||
|
description: 'Creates a new event.',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
label: 'Host',
|
||||||
|
key: 'hostId',
|
||||||
|
type: 'dropdown',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
source: {
|
||||||
|
type: 'query',
|
||||||
|
name: 'getDynamicData',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: 'key',
|
||||||
|
value: 'listContactOwners',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Title',
|
||||||
|
key: 'title',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'From',
|
||||||
|
key: 'from',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
description: 'The date format is ISO8601 (yyyy-mm-ddTHH:mm:ssZ).',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'To',
|
||||||
|
key: 'to',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
description: 'The date format is ISO8601 (yyyy-mm-ddTHH:mm:ssZ).',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'All Day',
|
||||||
|
key: 'allDay',
|
||||||
|
type: 'dropdown',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
options: [
|
||||||
|
{ label: 'True', value: true },
|
||||||
|
{ label: 'False', value: false },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Frequency (Recurring Activity)',
|
||||||
|
key: 'frequency',
|
||||||
|
type: 'dropdown',
|
||||||
|
required: false,
|
||||||
|
description:
|
||||||
|
'Specifies the frequency of event recurrence. The options include DAILY, WEEKLY, MONTHLY, or YEARLY.',
|
||||||
|
variables: true,
|
||||||
|
options: [
|
||||||
|
{ label: 'Daily Events', value: 'DAILY' },
|
||||||
|
{ label: 'Weekly Events', value: 'WEEKLY' },
|
||||||
|
{ label: 'Monthly Events', value: 'MONTHLY' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Interval (Recurring Activity)',
|
||||||
|
key: 'interval',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description:
|
||||||
|
'Specifies the time difference between individual events. The INTERVAL can be anywhere from 1 to 99. For instance, with a WEEKLY event set at an INTERVAL of 2, there will be a two-week gap between each occurrence.',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'By Month Day (Recurring Activity)',
|
||||||
|
key: 'byMonthDay',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description:
|
||||||
|
'This specifies the date within the month when the event recurs. The BYMONTHDAY value can be any number from 1 to 31. This rule applies exclusively to events that repeat monthly or yearly.',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'By Week (Recurring Activity)',
|
||||||
|
key: 'byWeek',
|
||||||
|
type: 'dropdown',
|
||||||
|
required: false,
|
||||||
|
description:
|
||||||
|
'Only relevant for events that occur on a monthly or yearly basis.',
|
||||||
|
variables: true,
|
||||||
|
options: [
|
||||||
|
{ label: 'First week of the month', value: '1' },
|
||||||
|
{ label: 'Second week of the month', value: '2' },
|
||||||
|
{ label: 'Third week of the month', value: '3' },
|
||||||
|
{ label: 'Fourth week of the month', value: '4' },
|
||||||
|
{ label: 'Last week of the month', value: '-1' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'By Day (Recurring Activity)',
|
||||||
|
key: 'byDay',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description:
|
||||||
|
'This signifies the weekday when the event recurs. The options include SU, MO, TU, WE, TH, FR, or SA. This rule applies to events that repeat daily, weekly, monthly, and yearly (should not be combined with INTERVAL).',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Count (Recurring Activity)',
|
||||||
|
key: 'count',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description:
|
||||||
|
'Specifies the number of events you wish to generate. The count value range from 1 to 99.',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Until (Recurring Activity)',
|
||||||
|
key: 'until',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description:
|
||||||
|
'Specifies the concluding date for the event recurrence. Please input the date in the YYYY-MM-DD format.',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Reminder',
|
||||||
|
key: 'reminder',
|
||||||
|
type: 'dropdown',
|
||||||
|
required: false,
|
||||||
|
description:
|
||||||
|
'Provide the reminder list to notify or prompt participants prior to the event.',
|
||||||
|
variables: true,
|
||||||
|
options: [
|
||||||
|
{ label: '5 min', value: '5 minutes' },
|
||||||
|
{ label: '10 min', value: '10 minutes' },
|
||||||
|
{ label: '15 min', value: '15 minutes' },
|
||||||
|
{ label: '1 hrs', value: '1 hours' },
|
||||||
|
{ label: '2 hrs', value: '2 hours' },
|
||||||
|
{ label: '1 days', value: '1 days' },
|
||||||
|
{ label: '2 days', value: '2 days' },
|
||||||
|
{ label: '1 weeks', value: '1 weeks' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Location',
|
||||||
|
key: 'location',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Related Module',
|
||||||
|
key: 'relatedModule',
|
||||||
|
type: 'dropdown',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
options: [
|
||||||
|
{ label: 'Companies', value: 'Accounts' },
|
||||||
|
{ label: 'Contacts', value: 'Contacts' },
|
||||||
|
{ label: 'Deals', value: 'Deals' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Participants',
|
||||||
|
key: 'participants',
|
||||||
|
type: 'dynamic',
|
||||||
|
required: false,
|
||||||
|
description: 'Email Address of participants.',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
label: 'Participant',
|
||||||
|
key: 'participant',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Description',
|
||||||
|
key: 'description',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Tags',
|
||||||
|
key: 'tags',
|
||||||
|
type: 'dynamic',
|
||||||
|
required: false,
|
||||||
|
description: '',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
label: 'Tag',
|
||||||
|
key: 'tag',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const {
|
||||||
|
hostId,
|
||||||
|
title,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
allDay,
|
||||||
|
frequency,
|
||||||
|
interval,
|
||||||
|
byMonthDay,
|
||||||
|
byDay,
|
||||||
|
byWeek,
|
||||||
|
count,
|
||||||
|
until,
|
||||||
|
reminder,
|
||||||
|
location,
|
||||||
|
relatedModule,
|
||||||
|
participants,
|
||||||
|
description,
|
||||||
|
tags,
|
||||||
|
} = $.step.parameters;
|
||||||
|
|
||||||
|
const allTags = tags.map((tag) => ({
|
||||||
|
name: tag.tag,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const allParticipants = participants.map((participant) => ({
|
||||||
|
type: 'email',
|
||||||
|
participant: participant.participant,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const [unit, period] = reminder.split(' ');
|
||||||
|
|
||||||
|
let rrule = `FREQ=${frequency};INTERVAL=${interval};`;
|
||||||
|
if (count) rrule += `COUNT=${count};`;
|
||||||
|
if (byMonthDay) rrule += `BYMONTHDAY=${byMonthDay};`;
|
||||||
|
if (byDay) rrule += `BYDAY=${byDay};`;
|
||||||
|
if (byWeek) rrule += `BYSETPOS=${byWeek};`;
|
||||||
|
if (until) rrule += `UNTIL=${until};`;
|
||||||
|
|
||||||
|
const body = {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
Owner: {
|
||||||
|
id: hostId,
|
||||||
|
},
|
||||||
|
Event_Title: title,
|
||||||
|
Start_DateTime: from,
|
||||||
|
End_DateTime: to,
|
||||||
|
All_day: allDay,
|
||||||
|
$se_module: relatedModule,
|
||||||
|
Recurring_Activity: {
|
||||||
|
RRULE: rrule,
|
||||||
|
},
|
||||||
|
Remind_At: [
|
||||||
|
{
|
||||||
|
unit: Number(unit),
|
||||||
|
period,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Venue: location,
|
||||||
|
Participants: allParticipants,
|
||||||
|
Description: description,
|
||||||
|
Tag: allTags,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data } = await $.http.post(`/bigin/v2/Events`, body);
|
||||||
|
|
||||||
|
$.setActionItem({
|
||||||
|
raw: data,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
@@ -0,0 +1,5 @@
|
|||||||
|
import createCompany from './create-company/index.js';
|
||||||
|
import createContact from './create-contact/index.js';
|
||||||
|
import createEvent from './create-event/index.js';
|
||||||
|
|
||||||
|
export default [createCompany, createContact, createEvent];
|
@@ -0,0 +1,32 @@
|
|||||||
|
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" id="b" data-name="Layer 2" width="327.714" height="120" viewBox="0 0 327.714 120">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.d {
|
||||||
|
fill: #039649;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<g id="c" data-name="Layer 1">
|
||||||
|
<g>
|
||||||
|
<path class="d" d="m52.39,120c-1.801,0-3.6-.6-5.101-1.5-2.1-1.5-3.6-4.2-3.6-6.9v-24.6L1.09,12.901C-.41,9.9-.41,6.601,1.391,4.2,2.891,1.5,5.89,0,8.89,0h78c2.999,0,5.698,1.5,7.199,4.2,1.801,2.702,1.801,6.3.301,9.002l-5.101,9h10.201c2.999,0,5.698,1.5,7.199,4.2,1.801,2.7,1.801,6.3.301,9l-29.701,51.3v18.599c0,3.899-2.399,6.9-5.999,8.099l-16.2,6.3c-.6.301-1.799.301-2.7.301M8.89,8.4q-.301.301-.301.602l42.301,73.798c1.199,2.1,1.199,3.299,1.199,4.501v23.998s.301.301.6,0l16.2-6.3h.6v-17.999c0-1.199,0-3.299,1.201-4.8l29.4-50.999c0-.301,0-.6-.301-.6l-53.699-.301,14.399,24.901,10.201-17.7c1.199-2.1,3.6-2.7,5.7-1.5,2.1,1.199,2.7,3.6,1.498,5.7l-11.998,20.699c-2.702,4.2-8.701,3.901-11.102.301l-17.4-30.9c-1.199-1.799-1.199-4.2,0-6.3,1.201-2.1,3.301-3.6,5.7-3.6h36.6l7.499-12.899s0-.301-.299-.602H8.89Z"/>
|
||||||
|
<g>
|
||||||
|
<path d="m181.725,48.737c0,3.089-.54,5.684-1.618,7.788-1.081,2.104-2.548,3.79-4.407,5.062-1.858,1.27-4.016,2.179-6.476,2.726-2.459.547-5.069.819-7.828.819h-24.591V6.521h23.034c2.676,0,5.198.24,7.561.718,2.364.479,4.427,1.311,6.189,2.5,1.762,1.189,3.149,2.787,4.16,4.795,1.011,2.008,1.517,4.53,1.517,7.562,0,3.17-.786,5.813-2.357,7.93-1.57,2.119-3.913,3.628-7.028,4.53,4.017.819,6.995,2.371,8.935,4.652s2.91,5.458,2.91,9.529Zm-33.813-17.624h10.533c1.612,0,3.033-.136,4.263-.41,1.23-.272,2.268-.738,3.115-1.393.846-.656,1.489-1.55,1.927-2.684.436-1.134.655-2.562.655-4.284,0-1.612-.267-2.923-.799-3.934-.532-1.01-1.25-1.809-2.152-2.398-.902-.587-1.967-.99-3.197-1.209s-2.542-.328-3.934-.328h-10.411v16.64Zm0,26.067h12.09c1.64,0,3.115-.169,4.427-.512,1.311-.342,2.424-.894,3.341-1.66.915-.764,1.612-1.748,2.091-2.951.478-1.202.716-2.663.716-4.385,0-1.802-.294-3.285-.881-4.447-.588-1.161-1.394-2.083-2.419-2.766s-2.227-1.154-3.606-1.414c-1.381-.26-2.863-.39-4.447-.39h-11.312v18.525Z"/>
|
||||||
|
<path d="m202.257,9.555c0,.847-.151,1.633-.451,2.356-.3.724-.716,1.34-1.25,1.844-.532.507-1.167.902-1.905,1.189s-1.53.431-2.377.431c-.819,0-1.598-.144-2.336-.431s-1.388-.69-1.947-1.209c-.56-.519-.998-1.133-1.311-1.844-.315-.711-.471-1.489-.471-2.336s.156-1.626.471-2.336c.314-.711.744-1.325,1.291-1.845.546-.519,1.187-.922,1.925-1.209s1.53-.431,2.377-.431c.821,0,1.598.144,2.336.431s1.373.69,1.907,1.209c.532.52.955,1.134,1.27,1.845.314.71.471,1.489.471,2.336Zm-.778,12.705v42.871h-10.246V22.261h10.246Z"/>
|
||||||
|
<path d="m254.186,61.935c0,3.361-.587,6.236-1.762,8.627-1.174,2.391-2.78,4.352-4.815,5.882-2.036,1.529-4.407,2.65-7.111,3.361-2.706.71-5.589,1.065-8.648,1.065-2.023,0-4.037-.157-6.045-.471-2.009-.314-3.908-.861-5.697-1.64-1.79-.778-3.395-1.837-4.816-3.175-1.421-1.34-2.555-3.021-3.402-5.042l8.074-3.525c.519,1.202,1.202,2.193,2.049,2.971.847.779,1.797,1.408,2.848,1.885,1.051.479,2.172.814,3.361,1.005s2.398.287,3.628.287c1.885,0,3.572-.232,5.062-.696,1.489-.466,2.752-1.169,3.79-2.111,1.039-.943,1.838-2.132,2.399-3.566.559-1.434.839-3.107.839-5.02v-6.393c-.71,1.229-1.592,2.336-2.643,3.319-1.053.983-2.207,1.824-3.464,2.52s-2.582,1.237-3.976,1.62c-1.393.383-2.814.574-4.263.574-3.033,0-5.737-.56-8.114-1.681-2.377-1.119-4.379-2.636-6.005-4.55-1.625-1.912-2.862-4.139-3.709-6.68-.847-2.542-1.27-5.233-1.27-8.074,0-2.814.451-5.491,1.353-8.033.901-2.542,2.192-4.775,3.873-6.702,1.68-1.927,3.709-3.464,6.086-4.611,2.376-1.147,5.054-1.721,8.033-1.721,2.923,0,5.621.594,8.094,1.782,2.472,1.189,4.473,3.082,6.004,5.677v-6.557h10.246v39.674Zm-33.198-19.017c0,1.666.252,3.265.758,4.795s1.237,2.876,2.193,4.037c.957,1.162,2.137,2.084,3.545,2.767s3.013,1.025,4.816,1.025c2.021,0,3.784-.355,5.287-1.066,1.502-.711,2.746-1.673,3.729-2.89.985-1.215,1.722-2.643,2.213-4.283.492-1.64.738-3.374.738-5.206,0-1.802-.239-3.49-.716-5.062-.479-1.57-1.203-2.93-2.173-4.077s-2.179-2.056-3.626-2.726c-1.449-.67-3.157-1.005-5.123-1.005-2.049,0-3.812.363-5.287,1.086-1.476.724-2.684,1.709-3.628,2.951-.943,1.243-1.633,2.699-2.069,4.365-.438,1.666-.656,3.429-.656,5.287Z"/>
|
||||||
|
<path d="m277.096,9.555c0,.847-.151,1.633-.451,2.356-.3.724-.716,1.34-1.25,1.844-.532.507-1.167.902-1.905,1.189s-1.53.431-2.377.431c-.819,0-1.598-.144-2.336-.431s-1.388-.69-1.947-1.209c-.56-.519-.998-1.133-1.311-1.844-.315-.711-.471-1.489-.471-2.336s.156-1.626.471-2.336c.314-.711.744-1.325,1.291-1.845.546-.519,1.187-.922,1.925-1.209s1.53-.431,2.377-.431c.821,0,1.598.144,2.336.431s1.373.69,1.907,1.209c.532.52.955,1.134,1.27,1.845.314.71.471,1.489.471,2.336Zm-.778,12.705v42.871h-10.246V22.261h10.246Z"/>
|
||||||
|
<path d="m308.451,29.801c-1.585,0-2.999.24-4.243.718-1.243.479-2.288,1.162-3.135,2.049-.847.889-1.496,1.961-1.947,3.217-.451,1.258-.676,2.651-.676,4.181v25.165h-10.246V22.261h10.246v6.803c.683-1.338,1.537-2.492,2.562-3.462,1.025-.97,2.165-1.769,3.422-2.399,1.257-.627,2.59-1.091,3.997-1.393,1.406-.3,2.82-.451,4.241-.451,2.623,0,4.884.431,6.783,1.291s3.464,2.049,4.694,3.565c1.229,1.517,2.131,3.307,2.704,5.37s.861,4.311.861,6.742v26.805h-10.328v-25.822c0-3.005-.711-5.341-2.132-7.008-1.421-1.666-3.688-2.5-6.803-2.5Z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path d="m152.871,102.12c0,1.138-.168,2.221-.507,3.25-.337,1.028-.828,1.935-1.471,2.72s-1.436,1.41-2.379,1.874-2.021.696-3.234.696c-.579,0-1.155-.061-1.723-.183-.569-.121-1.107-.306-1.613-.553-.505-.247-.974-.561-1.407-.941s-.801-.822-1.107-1.328v2.72h-2.625v-24.446h2.625v10.689c.273-.464.632-.882,1.076-1.257.442-.374.93-.696,1.463-.964.531-.27,1.082-.477,1.652-.626.569-.146,1.122-.22,1.66-.22,1.244,0,2.34.227,3.289.679.95.454,1.743,1.068,2.38,1.843s1.117,1.685,1.44,2.728c.321,1.044.482,2.151.482,3.321Zm-13.536.126c0,.917.123,1.756.371,2.515.249.759.614,1.415,1.1,1.968.485.553,1.079.984,1.787,1.289.706.306,1.517.459,2.435.459.991,0,1.813-.178,2.467-.53.653-.354,1.178-.828,1.573-1.424.395-.595.674-1.283.838-2.063.163-.78.245-1.603.245-2.467,0-.801-.104-1.576-.308-2.325-.206-.748-.52-1.415-.941-1.999-.422-.586-.958-1.055-1.605-1.407-.648-.354-1.415-.53-2.3-.53-.981,0-1.827.174-2.538.521-.711.349-1.3.818-1.764,1.409-.464.59-.806,1.28-1.028,2.071s-.332,1.629-.332,2.514Z"/>
|
||||||
|
<path d="m169.949,93.834l-9.141,22.297h-2.656l3.131-7.464-6.388-14.833h2.814l4.965,11.86,4.586-11.86h2.689Z"/>
|
||||||
|
<path d="m194.11,108.034v2.34h-16.414v-1.139l13.189-19.228h-12.05v-2.246h15.465v1.139l-12.84,19.134h12.65Z"/>
|
||||||
|
<path d="m213.229,102.215c0,1.202-.206,2.319-.617,3.352-.411,1.034-.986,1.927-1.723,2.681-.739.753-1.611,1.344-2.618,1.77-1.007.428-2.106.641-3.296.641-1.256,0-2.388-.222-3.4-.665s-1.874-1.053-2.585-1.834-1.257-1.697-1.637-2.752c-.38-1.053-.57-2.192-.57-3.416,0-1.191.201-2.3.601-3.329.4-1.028.964-1.92,1.692-2.68.727-.759,1.594-1.354,2.601-1.787s2.116-.648,3.329-.648c1.254,0,2.391.22,3.408.663s1.881,1.055,2.593,1.835,1.26,1.697,1.644,2.751c.385,1.055.578,2.192.578,3.416Zm-13.726.031c0,.886.117,1.708.349,2.467s.579,1.418,1.043,1.977c.464.558,1.038.995,1.723,1.311.685.317,1.481.476,2.388.476.938,0,1.753-.175,2.443-.522.691-.349,1.263-.816,1.717-1.407.452-.591.79-1.275,1.012-2.056.22-.78.332-1.602.332-2.466,0-.844-.117-1.646-.349-2.404-.232-.759-.579-1.429-1.043-2.008s-1.038-1.038-1.723-1.376c-.685-.337-1.481-.505-2.388-.505-.971,0-1.802.174-2.498.521-.696.349-1.265.82-1.708,1.416-.443.595-.77,1.288-.981,2.078-.21.792-.316,1.624-.316,2.498Z"/>
|
||||||
|
<path d="m224.423,95.827c-.696,0-1.323.105-1.881.316s-1.034.512-1.423.902c-.391.39-.691.864-.902,1.423s-.316,1.186-.316,1.881v10.026h-2.625v-24.446h2.625v10.531c.253-.516.564-.956.933-1.32s.777-.663,1.226-.902c.447-.237.933-.411,1.454-.521.522-.111,1.057-.166,1.605-.166,1.033,0,1.937.171,2.712.513.775.343,1.423.818,1.945,1.424.522.605.915,1.326,1.178,2.157.263.833.395,1.745.395,2.735v9.994h-2.625v-10.184c0-1.423-.36-2.506-1.083-3.25-.722-.742-1.795-1.114-3.217-1.114Z"/>
|
||||||
|
<path d="m251.685,102.215c0,1.202-.206,2.319-.617,3.352-.411,1.034-.986,1.927-1.723,2.681-.739.753-1.611,1.344-2.618,1.77-1.007.428-2.106.641-3.296.641-1.256,0-2.388-.222-3.4-.665s-1.874-1.053-2.585-1.834-1.257-1.697-1.637-2.752c-.38-1.053-.57-2.192-.57-3.416,0-1.191.201-2.3.601-3.329.4-1.028.964-1.92,1.692-2.68.727-.759,1.594-1.354,2.601-1.787s2.116-.648,3.329-.648c1.254,0,2.391.22,3.408.663s1.881,1.055,2.593,1.835,1.26,1.697,1.644,2.751c.385,1.055.578,2.192.578,3.416Zm-13.726.031c0,.886.117,1.708.349,2.467s.579,1.418,1.043,1.977c.464.558,1.038.995,1.723,1.311.685.317,1.481.476,2.388.476.938,0,1.753-.175,2.443-.522.691-.349,1.263-.816,1.717-1.407.452-.591.79-1.275,1.012-2.056.22-.78.332-1.602.332-2.466,0-.844-.117-1.646-.349-2.404-.232-.759-.579-1.429-1.043-2.008s-1.038-1.038-1.723-1.376c-.685-.337-1.481-.505-2.388-.505-.971,0-1.802.174-2.498.521-.696.349-1.265.82-1.708,1.416-.443.595-.77,1.288-.981,2.078-.21.792-.316,1.624-.316,2.498Z"/>
|
||||||
|
<path d="m281.097,103.923c-.38.959-.884,1.85-1.511,2.672-.627.823-1.346,1.534-2.157,2.135-.812.601-1.703,1.073-2.673,1.415-.969.342-1.976.514-3.02.514-1.76,0-3.312-.295-4.656-.886-1.345-.59-2.472-1.407-3.385-2.45-.912-1.044-1.603-2.279-2.072-3.709-.469-1.428-.704-2.98-.704-4.657,0-1.033.114-2.037.341-3.013.227-.974.553-1.889.98-2.743.428-.854.95-1.637,1.565-2.348.617-.711,1.318-1.32,2.104-1.827.785-.505,1.652-.901,2.601-1.186.949-.284,1.961-.426,3.036-.426,1.012,0,2.003.148,2.973.442.971.295,1.866.718,2.689,1.266.822.548,1.55,1.209,2.182,1.984s1.127,1.642,1.486,2.602l-2.246.98c-.295-.768-.673-1.468-1.13-2.095-.459-.627-.989-1.162-1.59-1.604-.601-.443-1.265-.785-1.992-1.029-.728-.242-1.508-.363-2.341-.363-1.359,0-2.532.268-3.518.806s-1.797,1.254-2.435,2.151c-.638.895-1.107,1.919-1.407,3.067-.301,1.149-.451,2.335-.451,3.558,0,1.244.166,2.424.498,3.543.333,1.117.833,2.098,1.503,2.94.669.844,1.507,1.513,2.514,2.008,1.007.496,2.18.744,3.518.744.801,0,1.576-.143,2.325-.428.749-.284,1.44-.671,2.072-1.162.632-.49,1.191-1.064,1.675-1.723.485-.658.864-1.357,1.139-2.095l2.088.917Z"/>
|
||||||
|
<path d="m287.991,100.539v9.835h-2.751v-22.613h7.431c1.044,0,2.042.111,2.997.332.954.222,1.795.586,2.522,1.092.728.505,1.307,1.168,1.74,1.984.431.818.648,1.822.648,3.013,0,.885-.137,1.673-.411,2.364-.275.691-.66,1.289-1.155,1.795-.496.507-1.086.925-1.771,1.257-.685.333-1.44.583-2.261.752l6.926,10.026h-3.068l-6.736-9.835h-4.112Zm0-2.183h4.871c.727,0,1.393-.075,1.999-.228s1.131-.402,1.574-.744c.442-.342.785-.785,1.028-1.328s.363-1.21.363-2.001c0-.801-.126-1.466-.378-1.992-.254-.527-.601-.943-1.044-1.25-.443-.305-.967-.521-1.573-.648s-1.262-.189-1.968-.189h-4.871v8.38Z"/>
|
||||||
|
<path d="m308.231,91.335v19.039h-2.529v-22.613h3.826l7.242,17.932,7.148-17.932h3.795v22.613h-2.752v-19.039l-7.653,19.039h-1.17l-7.907-19.039Z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 10 KiB |
@@ -0,0 +1,25 @@
|
|||||||
|
import { URLSearchParams } from 'url';
|
||||||
|
import authScope from '../common/auth-scope.js';
|
||||||
|
|
||||||
|
export default async function generateAuthUrl($) {
|
||||||
|
const oauthRedirectUrlField = $.app.auth.fields.find(
|
||||||
|
(field) => field.key == 'oAuthRedirectUrl'
|
||||||
|
);
|
||||||
|
const redirectUri = oauthRedirectUrlField.value;
|
||||||
|
const searchParams = new URLSearchParams({
|
||||||
|
scope: authScope.join(','),
|
||||||
|
client_id: $.auth.data.clientId,
|
||||||
|
response_type: 'code',
|
||||||
|
access_type: 'offline',
|
||||||
|
redirect_uri: redirectUri,
|
||||||
|
});
|
||||||
|
|
||||||
|
const domain =
|
||||||
|
$.auth.data.region !== 'cn' ? 'account.zoho.com' : 'accounts.zoho.com.cn';
|
||||||
|
|
||||||
|
const url = `https://${domain}/oauth/v2/auth?${searchParams.toString()}`;
|
||||||
|
|
||||||
|
await $.auth.set({
|
||||||
|
url,
|
||||||
|
});
|
||||||
|
}
|
66
packages/backend/src/apps/bigin-by-zoho-crm/auth/index.js
Normal file
66
packages/backend/src/apps/bigin-by-zoho-crm/auth/index.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import generateAuthUrl from './generate-auth-url.js';
|
||||||
|
import verifyCredentials from './verify-credentials.js';
|
||||||
|
import refreshToken from './refresh-token.js';
|
||||||
|
import isStillVerified from './is-still-verified.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
key: 'oAuthRedirectUrl',
|
||||||
|
label: 'OAuth Redirect URL',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
readOnly: true,
|
||||||
|
value: '{WEB_APP_URL}/app/bigin-by-zoho-crm/connections/add',
|
||||||
|
placeholder: null,
|
||||||
|
description:
|
||||||
|
'When asked to input a redirect URL in Bigin By Zoho CRM, enter the URL above.',
|
||||||
|
clickToCopy: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'region',
|
||||||
|
label: 'Region',
|
||||||
|
type: 'dropdown',
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description: '',
|
||||||
|
options: [
|
||||||
|
{ label: 'United States', value: 'us' },
|
||||||
|
{ label: 'European Union', value: 'eu' },
|
||||||
|
{ label: 'Australia', value: 'au' },
|
||||||
|
{ label: 'India', value: 'in' },
|
||||||
|
{ label: 'China', value: 'cn' },
|
||||||
|
],
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'clientId',
|
||||||
|
label: 'Client ID',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description: null,
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'clientSecret',
|
||||||
|
label: 'Client Secret',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description: null,
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
generateAuthUrl,
|
||||||
|
verifyCredentials,
|
||||||
|
isStillVerified,
|
||||||
|
refreshToken,
|
||||||
|
};
|
@@ -0,0 +1,8 @@
|
|||||||
|
import getCurrentOrganization from '../common/get-current-organization.js';
|
||||||
|
|
||||||
|
const isStillVerified = async ($) => {
|
||||||
|
const org = await getCurrentOrganization($);
|
||||||
|
return !!org.id;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default isStillVerified;
|
@@ -0,0 +1,34 @@
|
|||||||
|
import { URLSearchParams } from 'node:url';
|
||||||
|
|
||||||
|
import authScope from '../common/auth-scope.js';
|
||||||
|
import { regionUrlMap } from '../common/region-url-map.js';
|
||||||
|
|
||||||
|
const refreshToken = async ($) => {
|
||||||
|
const location = $.auth.data.location;
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
client_id: $.auth.data.clientId,
|
||||||
|
client_secret: $.auth.data.clientSecret,
|
||||||
|
refresh_token: $.auth.data.refreshToken,
|
||||||
|
grant_type: 'refresh_token',
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data } = await $.http.post(
|
||||||
|
`${regionUrlMap[location]}/oauth/v2/token`,
|
||||||
|
params.toString(),
|
||||||
|
{
|
||||||
|
additionalProperties: {
|
||||||
|
skipAddingBaseUrl: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await $.auth.set({
|
||||||
|
accessToken: data.access_token,
|
||||||
|
apiDomain: data.api_domain,
|
||||||
|
scope: authScope.join(','),
|
||||||
|
tokenType: data.token_type,
|
||||||
|
expiresIn: data.expires_in,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default refreshToken;
|
@@ -0,0 +1,46 @@
|
|||||||
|
import { URLSearchParams } from 'node:url';
|
||||||
|
import { regionUrlMap } from '../common/region-url-map.js';
|
||||||
|
import getCurrentOrganization from '../common/get-current-organization.js';
|
||||||
|
|
||||||
|
const verifyCredentials = async ($) => {
|
||||||
|
const oauthRedirectUrlField = $.app.auth.fields.find(
|
||||||
|
(field) => field.key == 'oAuthRedirectUrl'
|
||||||
|
);
|
||||||
|
const redirectUri = oauthRedirectUrlField.value;
|
||||||
|
const location = $.auth.data.location;
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
client_id: $.auth.data.clientId,
|
||||||
|
client_secret: $.auth.data.clientSecret,
|
||||||
|
code: $.auth.data.code,
|
||||||
|
redirect_uri: redirectUri,
|
||||||
|
grant_type: 'authorization_code',
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data } = await $.http.post(
|
||||||
|
`${regionUrlMap[location]}/oauth/v2/token`,
|
||||||
|
params.toString()
|
||||||
|
);
|
||||||
|
|
||||||
|
await $.auth.set({
|
||||||
|
accessToken: data.access_token,
|
||||||
|
tokenType: data.token_type,
|
||||||
|
apiDomain: data.api_domain,
|
||||||
|
});
|
||||||
|
|
||||||
|
const organization = await getCurrentOrganization($);
|
||||||
|
|
||||||
|
const screenName = [organization.company_name, organization.primary_email]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' @ ');
|
||||||
|
|
||||||
|
await $.auth.set({
|
||||||
|
clientId: $.auth.data.clientId,
|
||||||
|
clientSecret: $.auth.data.clientSecret,
|
||||||
|
scope: $.auth.data.scope,
|
||||||
|
expiresIn: data.expires_in,
|
||||||
|
refreshToken: data.refresh_token,
|
||||||
|
screenName,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default verifyCredentials;
|
@@ -0,0 +1,9 @@
|
|||||||
|
const addAuthHeader = ($, requestConfig) => {
|
||||||
|
if ($.auth.data?.accessToken) {
|
||||||
|
requestConfig.headers.Authorization = `Zoho-oauthtoken ${$.auth.data.accessToken}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return requestConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default addAuthHeader;
|
@@ -0,0 +1,10 @@
|
|||||||
|
const authScope = [
|
||||||
|
'ZohoBigin.notifications.ALL',
|
||||||
|
'ZohoBigin.users.ALL',
|
||||||
|
'ZohoBigin.modules.ALL',
|
||||||
|
'ZohoBigin.org.READ',
|
||||||
|
'ZohoBigin.settings.ALL',
|
||||||
|
'ZohoBigin.modules.ALL',
|
||||||
|
];
|
||||||
|
|
||||||
|
export default authScope;
|
@@ -0,0 +1,6 @@
|
|||||||
|
const getCurrentOrganization = async ($) => {
|
||||||
|
const response = await $.http.get('/bigin/v2/org');
|
||||||
|
return response.data.org[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getCurrentOrganization;
|
@@ -0,0 +1,8 @@
|
|||||||
|
export const regionUrlMap = {
|
||||||
|
us: 'https://accounts.zoho.com',
|
||||||
|
au: 'https://accounts.zoho.com.au',
|
||||||
|
eu: 'https://accounts.zoho.eu',
|
||||||
|
in: 'https://accounts.zoho.in',
|
||||||
|
cn: 'https://accounts.zoho.com.cn',
|
||||||
|
jp: 'https://accounts.zoho.jp',
|
||||||
|
};
|
@@ -0,0 +1,14 @@
|
|||||||
|
const setBaseUrl = ($, requestConfig) => {
|
||||||
|
if (requestConfig.additionalProperties?.skipAddingBaseUrl)
|
||||||
|
return requestConfig;
|
||||||
|
|
||||||
|
const apiDomain = $.auth.data.apiDomain;
|
||||||
|
|
||||||
|
if (apiDomain) {
|
||||||
|
requestConfig.baseURL = apiDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
return requestConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default setBaseUrl;
|
@@ -0,0 +1,5 @@
|
|||||||
|
import listCompanies from './list-companies/index.js';
|
||||||
|
import listOrganizations from './list-organizations/index.js';
|
||||||
|
import listContactOwners from './list-contact-owners/index.js';
|
||||||
|
|
||||||
|
export default [listCompanies, listOrganizations, listContactOwners];
|
@@ -0,0 +1,39 @@
|
|||||||
|
export default {
|
||||||
|
name: 'List companies',
|
||||||
|
key: 'listCompanies',
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const companies = {
|
||||||
|
data: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
page: 1,
|
||||||
|
pageSize: 200,
|
||||||
|
fields: 'Account_Name',
|
||||||
|
});
|
||||||
|
|
||||||
|
let next = false;
|
||||||
|
do {
|
||||||
|
const { data } = await $.http.get('/bigin/v2/Accounts', { params });
|
||||||
|
|
||||||
|
if (data.info.more_records) {
|
||||||
|
params.page = params.page + 1;
|
||||||
|
next = true;
|
||||||
|
} else {
|
||||||
|
next = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.data) {
|
||||||
|
for (const account of data.data) {
|
||||||
|
companies.data.push({
|
||||||
|
value: account.id,
|
||||||
|
name: account.Account_Name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (next);
|
||||||
|
|
||||||
|
return companies;
|
||||||
|
},
|
||||||
|
};
|
@@ -0,0 +1,39 @@
|
|||||||
|
export default {
|
||||||
|
name: 'List contact owners',
|
||||||
|
key: 'listContactOwners',
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const contactOwners = {
|
||||||
|
data: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
type: 'AllUsers',
|
||||||
|
page: 1,
|
||||||
|
pageSize: 200,
|
||||||
|
};
|
||||||
|
|
||||||
|
let next = false;
|
||||||
|
do {
|
||||||
|
const { data } = await $.http.get('/bigin/v2/users', params);
|
||||||
|
|
||||||
|
if (data.users.length === params.pageSize) {
|
||||||
|
next = true;
|
||||||
|
params.page = params.page + 1;
|
||||||
|
} else {
|
||||||
|
next = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.users) {
|
||||||
|
for (const user of data.users) {
|
||||||
|
contactOwners.data.push({
|
||||||
|
value: user.id,
|
||||||
|
name: user.full_name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (next);
|
||||||
|
|
||||||
|
return contactOwners;
|
||||||
|
},
|
||||||
|
};
|
@@ -0,0 +1,23 @@
|
|||||||
|
export default {
|
||||||
|
name: 'List organizations',
|
||||||
|
key: 'listOrganizations',
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const organizations = {
|
||||||
|
data: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data } = await $.http.get('/bigin/v2/org');
|
||||||
|
|
||||||
|
if (data.org) {
|
||||||
|
for (const org of data.org) {
|
||||||
|
organizations.data.push({
|
||||||
|
value: org.id,
|
||||||
|
name: org.company_name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return organizations;
|
||||||
|
},
|
||||||
|
};
|
23
packages/backend/src/apps/bigin-by-zoho-crm/index.js
Normal file
23
packages/backend/src/apps/bigin-by-zoho-crm/index.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import defineApp from '../../helpers/define-app.js';
|
||||||
|
import addAuthHeader from './common/add-auth-header.js';
|
||||||
|
import auth from './auth/index.js';
|
||||||
|
import setBaseUrl from './common/set-base-url.js';
|
||||||
|
import triggers from './triggers/index.js';
|
||||||
|
import dynamicData from './dynamic-data/index.js';
|
||||||
|
import actions from './actions/index.js';
|
||||||
|
|
||||||
|
export default defineApp({
|
||||||
|
name: 'Bigin By Zoho CRM',
|
||||||
|
key: 'bigin-by-zoho-crm',
|
||||||
|
baseUrl: 'https://www.bigin.com',
|
||||||
|
apiBaseUrl: '',
|
||||||
|
iconUrl: '{BASE_URL}/apps/bigin-by-zoho-crm/assets/favicon.svg',
|
||||||
|
authDocUrl: 'https://automatisch.io/docs/apps/bigin-by-zoho-crm/connection',
|
||||||
|
primaryColor: '039649',
|
||||||
|
supportsConnections: true,
|
||||||
|
beforeRequest: [setBaseUrl, addAuthHeader],
|
||||||
|
auth,
|
||||||
|
triggers,
|
||||||
|
dynamicData,
|
||||||
|
actions,
|
||||||
|
});
|
@@ -0,0 +1,7 @@
|
|||||||
|
import newCalls from './new-calls/index.js';
|
||||||
|
import newCompanies from './new-companies/index.js';
|
||||||
|
import newContacts from './new-contacts/index.js';
|
||||||
|
import newProducts from './new-products/index.js';
|
||||||
|
import newTasks from './new-tasks/index.js';
|
||||||
|
|
||||||
|
export default [newCalls, newCompanies, newContacts, newProducts, newTasks];
|
@@ -0,0 +1,89 @@
|
|||||||
|
import Crypto from 'crypto';
|
||||||
|
import defineTrigger from '../../../../helpers/define-trigger.js';
|
||||||
|
|
||||||
|
export default defineTrigger({
|
||||||
|
name: 'New calls',
|
||||||
|
key: 'newCalls',
|
||||||
|
type: 'webhook',
|
||||||
|
description: 'Triggers when a new call is added.',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
label: 'Organization',
|
||||||
|
key: 'organizationId',
|
||||||
|
type: 'dropdown',
|
||||||
|
required: true,
|
||||||
|
description: '',
|
||||||
|
variables: false,
|
||||||
|
source: {
|
||||||
|
type: 'query',
|
||||||
|
name: 'getDynamicData',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: 'key',
|
||||||
|
value: 'listOrganizations',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const dataItem = {
|
||||||
|
raw: $.request.body,
|
||||||
|
meta: {
|
||||||
|
internalId: Crypto.randomUUID(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
$.pushTriggerItem(dataItem);
|
||||||
|
},
|
||||||
|
|
||||||
|
async testRun($) {
|
||||||
|
const organizationId = $.step.parameters.organizationId;
|
||||||
|
|
||||||
|
const sampleEventData = {
|
||||||
|
ids: ['111111111111111111'],
|
||||||
|
token: null,
|
||||||
|
module: 'Calls',
|
||||||
|
operation: 'insert',
|
||||||
|
channel_id: organizationId,
|
||||||
|
server_time: 1708426963120,
|
||||||
|
query_params: {},
|
||||||
|
resource_uri: `${$.auth.data.apiDomain}/bigin/v1/Calls`,
|
||||||
|
affected_fields: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const dataItem = {
|
||||||
|
raw: sampleEventData,
|
||||||
|
meta: {
|
||||||
|
internalId: sampleEventData.channel_id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
$.pushTriggerItem(dataItem);
|
||||||
|
},
|
||||||
|
|
||||||
|
async registerHook($) {
|
||||||
|
const organizationId = $.step.parameters.organizationId;
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
watch: [
|
||||||
|
{
|
||||||
|
channel_id: organizationId,
|
||||||
|
notify_url: $.webhookUrl,
|
||||||
|
events: ['Calls.create'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
await $.http.post('/bigin/v2/actions/watch', payload);
|
||||||
|
|
||||||
|
await $.flow.setRemoteWebhookId(organizationId);
|
||||||
|
},
|
||||||
|
|
||||||
|
async unregisterHook($) {
|
||||||
|
await $.http.delete(
|
||||||
|
`/bigin/v2/actions/watch?channel_ids=${$.flow.remoteWebhookId}`
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
@@ -0,0 +1,89 @@
|
|||||||
|
import Crypto from 'crypto';
|
||||||
|
import defineTrigger from '../../../../helpers/define-trigger.js';
|
||||||
|
|
||||||
|
export default defineTrigger({
|
||||||
|
name: 'New companies',
|
||||||
|
key: 'newCompanies',
|
||||||
|
type: 'webhook',
|
||||||
|
description: 'Triggers when a new company is created.',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
label: 'Organization',
|
||||||
|
key: 'organizationId',
|
||||||
|
type: 'dropdown',
|
||||||
|
required: true,
|
||||||
|
description: '',
|
||||||
|
variables: false,
|
||||||
|
source: {
|
||||||
|
type: 'query',
|
||||||
|
name: 'getDynamicData',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: 'key',
|
||||||
|
value: 'listOrganizations',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const dataItem = {
|
||||||
|
raw: $.request.body,
|
||||||
|
meta: {
|
||||||
|
internalId: Crypto.randomUUID(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
$.pushTriggerItem(dataItem);
|
||||||
|
},
|
||||||
|
|
||||||
|
async testRun($) {
|
||||||
|
const organizationId = $.step.parameters.organizationId;
|
||||||
|
|
||||||
|
const sampleEventData = {
|
||||||
|
ids: ['111111111111111111'],
|
||||||
|
token: null,
|
||||||
|
module: 'Accounts',
|
||||||
|
operation: 'insert',
|
||||||
|
channel_id: organizationId,
|
||||||
|
server_time: 1708426963120,
|
||||||
|
query_params: {},
|
||||||
|
resource_uri: `${$.auth.data.apiDomain}/bigin/v1/Accounts`,
|
||||||
|
affected_fields: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const dataItem = {
|
||||||
|
raw: sampleEventData,
|
||||||
|
meta: {
|
||||||
|
internalId: sampleEventData.channel_id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
$.pushTriggerItem(dataItem);
|
||||||
|
},
|
||||||
|
|
||||||
|
async registerHook($) {
|
||||||
|
const organizationId = $.step.parameters.organizationId;
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
watch: [
|
||||||
|
{
|
||||||
|
channel_id: organizationId,
|
||||||
|
notify_url: $.webhookUrl,
|
||||||
|
events: ['Accounts.create'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
await $.http.post('/bigin/v2/actions/watch', payload);
|
||||||
|
|
||||||
|
await $.flow.setRemoteWebhookId(organizationId);
|
||||||
|
},
|
||||||
|
|
||||||
|
async unregisterHook($) {
|
||||||
|
await $.http.delete(
|
||||||
|
`/bigin/v2/actions/watch?channel_ids=${$.flow.remoteWebhookId}`
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
@@ -0,0 +1,89 @@
|
|||||||
|
import Crypto from 'crypto';
|
||||||
|
import defineTrigger from '../../../../helpers/define-trigger.js';
|
||||||
|
|
||||||
|
export default defineTrigger({
|
||||||
|
name: 'New contacts',
|
||||||
|
key: 'newContacts',
|
||||||
|
type: 'webhook',
|
||||||
|
description: 'Triggers when a new contact is created.',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
label: 'Organization',
|
||||||
|
key: 'organizationId',
|
||||||
|
type: 'dropdown',
|
||||||
|
required: true,
|
||||||
|
description: '',
|
||||||
|
variables: false,
|
||||||
|
source: {
|
||||||
|
type: 'query',
|
||||||
|
name: 'getDynamicData',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: 'key',
|
||||||
|
value: 'listOrganizations',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const dataItem = {
|
||||||
|
raw: $.request.body,
|
||||||
|
meta: {
|
||||||
|
internalId: Crypto.randomUUID(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
$.pushTriggerItem(dataItem);
|
||||||
|
},
|
||||||
|
|
||||||
|
async testRun($) {
|
||||||
|
const organizationId = $.step.parameters.organizationId;
|
||||||
|
|
||||||
|
const sampleEventData = {
|
||||||
|
ids: ['111111111111111111'],
|
||||||
|
token: null,
|
||||||
|
module: 'Contacts',
|
||||||
|
operation: 'insert',
|
||||||
|
channel_id: organizationId,
|
||||||
|
server_time: 1708426963120,
|
||||||
|
query_params: {},
|
||||||
|
resource_uri: `${$.auth.data.apiDomain}/bigin/v1/Contacts`,
|
||||||
|
affected_fields: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const dataItem = {
|
||||||
|
raw: sampleEventData,
|
||||||
|
meta: {
|
||||||
|
internalId: sampleEventData.channel_id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
$.pushTriggerItem(dataItem);
|
||||||
|
},
|
||||||
|
|
||||||
|
async registerHook($) {
|
||||||
|
const organizationId = $.step.parameters.organizationId;
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
watch: [
|
||||||
|
{
|
||||||
|
channel_id: organizationId,
|
||||||
|
notify_url: $.webhookUrl,
|
||||||
|
events: ['Contacts.create'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
await $.http.post('/bigin/v2/actions/watch', payload);
|
||||||
|
|
||||||
|
await $.flow.setRemoteWebhookId(organizationId);
|
||||||
|
},
|
||||||
|
|
||||||
|
async unregisterHook($) {
|
||||||
|
await $.http.delete(
|
||||||
|
`/bigin/v2/actions/watch?channel_ids=${$.flow.remoteWebhookId}`
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
@@ -0,0 +1,89 @@
|
|||||||
|
import Crypto from 'crypto';
|
||||||
|
import defineTrigger from '../../../../helpers/define-trigger.js';
|
||||||
|
|
||||||
|
export default defineTrigger({
|
||||||
|
name: 'New products',
|
||||||
|
key: 'newProducts',
|
||||||
|
type: 'webhook',
|
||||||
|
description: 'Triggers when a new product is created.',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
label: 'Organization',
|
||||||
|
key: 'organizationId',
|
||||||
|
type: 'dropdown',
|
||||||
|
required: true,
|
||||||
|
description: '',
|
||||||
|
variables: false,
|
||||||
|
source: {
|
||||||
|
type: 'query',
|
||||||
|
name: 'getDynamicData',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: 'key',
|
||||||
|
value: 'listOrganizations',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const dataItem = {
|
||||||
|
raw: $.request.body,
|
||||||
|
meta: {
|
||||||
|
internalId: Crypto.randomUUID(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
$.pushTriggerItem(dataItem);
|
||||||
|
},
|
||||||
|
|
||||||
|
async testRun($) {
|
||||||
|
const organizationId = $.step.parameters.organizationId;
|
||||||
|
|
||||||
|
const sampleEventData = {
|
||||||
|
ids: ['111111111111111111'],
|
||||||
|
token: null,
|
||||||
|
module: 'Products',
|
||||||
|
operation: 'insert',
|
||||||
|
channel_id: organizationId,
|
||||||
|
server_time: 1708426963120,
|
||||||
|
query_params: {},
|
||||||
|
resource_uri: `${$.auth.data.apiDomain}/bigin/v1/Products`,
|
||||||
|
affected_fields: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const dataItem = {
|
||||||
|
raw: sampleEventData,
|
||||||
|
meta: {
|
||||||
|
internalId: sampleEventData.channel_id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
$.pushTriggerItem(dataItem);
|
||||||
|
},
|
||||||
|
|
||||||
|
async registerHook($) {
|
||||||
|
const organizationId = $.step.parameters.organizationId;
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
watch: [
|
||||||
|
{
|
||||||
|
channel_id: organizationId,
|
||||||
|
notify_url: $.webhookUrl,
|
||||||
|
events: ['Products.create'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
await $.http.post('/bigin/v2/actions/watch', payload);
|
||||||
|
|
||||||
|
await $.flow.setRemoteWebhookId(organizationId);
|
||||||
|
},
|
||||||
|
|
||||||
|
async unregisterHook($) {
|
||||||
|
await $.http.delete(
|
||||||
|
`/bigin/v2/actions/watch?channel_ids=${$.flow.remoteWebhookId}`
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
@@ -0,0 +1,89 @@
|
|||||||
|
import Crypto from 'crypto';
|
||||||
|
import defineTrigger from '../../../../helpers/define-trigger.js';
|
||||||
|
|
||||||
|
export default defineTrigger({
|
||||||
|
name: 'New tasks',
|
||||||
|
key: 'newTasks',
|
||||||
|
type: 'webhook',
|
||||||
|
description: 'Triggers when a new task is created.',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
label: 'Organization',
|
||||||
|
key: 'organizationId',
|
||||||
|
type: 'dropdown',
|
||||||
|
required: true,
|
||||||
|
description: '',
|
||||||
|
variables: false,
|
||||||
|
source: {
|
||||||
|
type: 'query',
|
||||||
|
name: 'getDynamicData',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: 'key',
|
||||||
|
value: 'listOrganizations',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const dataItem = {
|
||||||
|
raw: $.request.body,
|
||||||
|
meta: {
|
||||||
|
internalId: Crypto.randomUUID(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
$.pushTriggerItem(dataItem);
|
||||||
|
},
|
||||||
|
|
||||||
|
async testRun($) {
|
||||||
|
const organizationId = $.step.parameters.organizationId;
|
||||||
|
|
||||||
|
const sampleEventData = {
|
||||||
|
ids: ['111111111111111111'],
|
||||||
|
token: null,
|
||||||
|
module: 'Tasks',
|
||||||
|
operation: 'insert',
|
||||||
|
channel_id: organizationId,
|
||||||
|
server_time: 1708426963120,
|
||||||
|
query_params: {},
|
||||||
|
resource_uri: `${$.auth.data.apiDomain}/bigin/v1/Tasks`,
|
||||||
|
affected_fields: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const dataItem = {
|
||||||
|
raw: sampleEventData,
|
||||||
|
meta: {
|
||||||
|
internalId: sampleEventData.channel_id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
$.pushTriggerItem(dataItem);
|
||||||
|
},
|
||||||
|
|
||||||
|
async registerHook($) {
|
||||||
|
const organizationId = $.step.parameters.organizationId;
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
watch: [
|
||||||
|
{
|
||||||
|
channel_id: organizationId,
|
||||||
|
notify_url: $.webhookUrl,
|
||||||
|
events: ['Tasks.create'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
await $.http.post('/bigin/v2/actions/watch', payload);
|
||||||
|
|
||||||
|
await $.flow.setRemoteWebhookId(organizationId);
|
||||||
|
},
|
||||||
|
|
||||||
|
async unregisterHook($) {
|
||||||
|
await $.http.delete(
|
||||||
|
`/bigin/v2/actions/watch?channel_ids=${$.flow.remoteWebhookId}`
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
@@ -0,0 +1,6 @@
|
|||||||
|
import appConfig from '../../../../config/app.js';
|
||||||
|
import { renderObject } from '../../../../helpers/renderer.js';
|
||||||
|
|
||||||
|
export default async (request, response) => {
|
||||||
|
renderObject(response, { version: appConfig.version });
|
||||||
|
};
|
@@ -0,0 +1,26 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import request from 'supertest';
|
||||||
|
import app from '../../../../app.js';
|
||||||
|
|
||||||
|
describe('GET /api/v1/automatisch/version', () => {
|
||||||
|
it('should return Automatisch version', async () => {
|
||||||
|
const response = await request(app)
|
||||||
|
.get('/api/v1/automatisch/version')
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const expectedPayload = {
|
||||||
|
data: {
|
||||||
|
version: '0.10.0',
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
count: 1,
|
||||||
|
currentPage: null,
|
||||||
|
isArray: false,
|
||||||
|
totalPages: null,
|
||||||
|
type: 'Object',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(response.body).toEqual(expectedPayload);
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,5 @@
|
|||||||
|
import { renderObject } from '../../../../helpers/renderer.js';
|
||||||
|
|
||||||
|
export default async (request, response) => {
|
||||||
|
renderObject(response, request.currentUser);
|
||||||
|
};
|
@@ -0,0 +1,26 @@
|
|||||||
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import request from 'supertest';
|
||||||
|
import app from '../../../../app.js';
|
||||||
|
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id';
|
||||||
|
import { createUser } from '../../../../../test/factories/user';
|
||||||
|
import getCurrentUserMock from '../../../../../test/mocks/rest/api/v1/users/get-current-user';
|
||||||
|
|
||||||
|
describe('GET /api/v1/users/me', () => {
|
||||||
|
let role, currentUser, token;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
currentUser = await createUser();
|
||||||
|
role = await currentUser.$relatedQuery('role');
|
||||||
|
token = createAuthTokenByUserId(currentUser.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return current user info', async () => {
|
||||||
|
const response = await request(app)
|
||||||
|
.get('/api/v1/users/me')
|
||||||
|
.set('Authorization', token)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const expectedPayload = getCurrentUserMock(currentUser, role);
|
||||||
|
expect(response.body).toEqual(expectedPayload);
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,12 @@
|
|||||||
|
import { renderObject } from '../../../../helpers/renderer.js';
|
||||||
|
|
||||||
|
export default async (request, response) => {
|
||||||
|
const inTrial = await request.currentUser.inTrial();
|
||||||
|
|
||||||
|
const trialInfo = {
|
||||||
|
inTrial,
|
||||||
|
expireAt: request.currentUser.trialExpiryDate,
|
||||||
|
};
|
||||||
|
|
||||||
|
renderObject(response, trialInfo);
|
||||||
|
};
|
@@ -0,0 +1,38 @@
|
|||||||
|
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import request from 'supertest';
|
||||||
|
import app from '../../../../app.js';
|
||||||
|
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id.js';
|
||||||
|
import { createUser } from '../../../../../test/factories/user.js';
|
||||||
|
import getUserTrialMock from '../../../../../test/mocks/rest/api/v1/users/get-user-trial.js';
|
||||||
|
import appConfig from '../../../../config/app.js';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import User from '../../../../models/user.js';
|
||||||
|
|
||||||
|
describe('GET /api/v1/users/:userId/trial', () => {
|
||||||
|
let user, token;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const trialExpiryDate = DateTime.now().plus({ days: 30 }).toISODate();
|
||||||
|
user = await createUser({ trialExpiryDate });
|
||||||
|
token = createAuthTokenByUserId(user.id);
|
||||||
|
|
||||||
|
vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('should return in trial, active subscription and expire at info', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
vi.spyOn(User.prototype, 'inTrial').mockResolvedValue(false);
|
||||||
|
vi.spyOn(User.prototype, 'hasActiveSubscription').mockResolvedValue(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return null', async () => {
|
||||||
|
const response = await request(app)
|
||||||
|
.get(`/api/v1/users/${user.id}/trial`)
|
||||||
|
.set('Authorization', token)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const expectedResponsePayload = await getUserTrialMock(user);
|
||||||
|
expect(response.body).toEqual(expectedResponsePayload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
16
packages/backend/src/controllers/api/v1/users/get-user.js
Normal file
16
packages/backend/src/controllers/api/v1/users/get-user.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { renderObject } from '../../../../helpers/renderer.js';
|
||||||
|
import User from '../../../../models/user.js';
|
||||||
|
|
||||||
|
export default async (request, response) => {
|
||||||
|
const user = await User.query()
|
||||||
|
.leftJoinRelated({
|
||||||
|
role: true,
|
||||||
|
})
|
||||||
|
.withGraphFetched({
|
||||||
|
role: true,
|
||||||
|
})
|
||||||
|
.findById(request.params.userId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
renderObject(response, user);
|
||||||
|
};
|
@@ -0,0 +1,36 @@
|
|||||||
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import request from 'supertest';
|
||||||
|
import app from '../../../../app.js';
|
||||||
|
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id';
|
||||||
|
import { createUser } from '../../../../../test/factories/user';
|
||||||
|
import { createPermission } from '../../../../../test/factories/permission';
|
||||||
|
import getUserMock from '../../../../../test/mocks/rest/api/v1/users/get-user';
|
||||||
|
|
||||||
|
describe('GET /api/v1/users/:userId', () => {
|
||||||
|
let currentUser, currentUserRole, anotherUser, anotherUserRole, token;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
currentUser = await createUser();
|
||||||
|
anotherUser = await createUser();
|
||||||
|
currentUserRole = await currentUser.$relatedQuery('role');
|
||||||
|
anotherUserRole = await anotherUser.$relatedQuery('role');
|
||||||
|
|
||||||
|
await createPermission({
|
||||||
|
roleId: currentUserRole.id,
|
||||||
|
action: 'read',
|
||||||
|
subject: 'User',
|
||||||
|
});
|
||||||
|
|
||||||
|
token = createAuthTokenByUserId(currentUser.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return specified user info', async () => {
|
||||||
|
const response = await request(app)
|
||||||
|
.get(`/api/v1/users/${anotherUser.id}`)
|
||||||
|
.set('Authorization', token)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const expectedPayload = getUserMock(anotherUser, anotherUserRole);
|
||||||
|
expect(response.body).toEqual(expectedPayload);
|
||||||
|
});
|
||||||
|
});
|
18
packages/backend/src/controllers/api/v1/users/get-users.js
Normal file
18
packages/backend/src/controllers/api/v1/users/get-users.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { renderObject } from '../../../../helpers/renderer.js';
|
||||||
|
import User from '../../../../models/user.js';
|
||||||
|
import paginateRest from '../../../../helpers/pagination-rest.js';
|
||||||
|
|
||||||
|
export default async (request, response) => {
|
||||||
|
const usersQuery = User.query()
|
||||||
|
.leftJoinRelated({
|
||||||
|
role: true,
|
||||||
|
})
|
||||||
|
.withGraphFetched({
|
||||||
|
role: true,
|
||||||
|
})
|
||||||
|
.orderBy('full_name', 'asc');
|
||||||
|
|
||||||
|
const users = await paginateRest(usersQuery, request.query.page);
|
||||||
|
|
||||||
|
renderObject(response, users);
|
||||||
|
};
|
@@ -0,0 +1,56 @@
|
|||||||
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import request from 'supertest';
|
||||||
|
import app from '../../../../app';
|
||||||
|
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id';
|
||||||
|
import { createRole } from '../../../../../test/factories/role';
|
||||||
|
import { createPermission } from '../../../../../test/factories/permission';
|
||||||
|
import { createUser } from '../../../../../test/factories/user';
|
||||||
|
import getUsersMock from '../../../../../test/mocks/rest/api/v1/users/get-users';
|
||||||
|
|
||||||
|
describe('GET /api/v1/users', () => {
|
||||||
|
let currentUser, currentUserRole, anotherUser, anotherUserRole, token;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
currentUserRole = await createRole({
|
||||||
|
key: 'currentUser',
|
||||||
|
name: 'Current user role',
|
||||||
|
});
|
||||||
|
|
||||||
|
await createPermission({
|
||||||
|
action: 'read',
|
||||||
|
subject: 'User',
|
||||||
|
roleId: currentUserRole.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
currentUser = await createUser({
|
||||||
|
roleId: currentUserRole.id,
|
||||||
|
fullName: 'Current User',
|
||||||
|
});
|
||||||
|
|
||||||
|
anotherUserRole = await createRole({
|
||||||
|
key: 'anotherUser',
|
||||||
|
name: 'Another user role',
|
||||||
|
});
|
||||||
|
|
||||||
|
anotherUser = await createUser({
|
||||||
|
roleId: anotherUserRole.id,
|
||||||
|
fullName: 'Another User',
|
||||||
|
});
|
||||||
|
|
||||||
|
token = createAuthTokenByUserId(currentUser.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return users data', async () => {
|
||||||
|
const response = await request(app)
|
||||||
|
.get('/api/v1/users')
|
||||||
|
.set('Authorization', token)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const expectedResponsePayload = await getUsersMock(
|
||||||
|
[anotherUser, currentUser],
|
||||||
|
[anotherUserRole, currentUserRole]
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(response.body).toEqual(expectedResponsePayload);
|
||||||
|
});
|
||||||
|
});
|
3
packages/backend/src/controllers/healthcheck/index.js
Normal file
3
packages/backend/src/controllers/healthcheck/index.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export default async (request, response) => {
|
||||||
|
response.status(200).end();
|
||||||
|
};
|
@@ -0,0 +1,9 @@
|
|||||||
|
import { describe, it } from 'vitest';
|
||||||
|
import request from 'supertest';
|
||||||
|
import app from '../../app.js';
|
||||||
|
|
||||||
|
describe('GET /healthcheck', () => {
|
||||||
|
it('should return 200 response with version data', async () => {
|
||||||
|
await request(app).get('/healthcheck').expect(200);
|
||||||
|
});
|
||||||
|
});
|
@@ -20,7 +20,8 @@ export const isAuthenticated = async (_parent, _args, req) => {
|
|||||||
.withGraphFetched({
|
.withGraphFetched({
|
||||||
role: true,
|
role: true,
|
||||||
permissions: true,
|
permissions: true,
|
||||||
});
|
})
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -28,6 +29,14 @@ export const isAuthenticated = async (_parent, _args, req) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const authenticateUser = async (request, response, next) => {
|
||||||
|
if (await isAuthenticated(null, null, request)) {
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
return response.status(401).end();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const isAuthenticatedRule = rule()(isAuthenticated);
|
const isAuthenticatedRule = rule()(isAuthenticated);
|
||||||
|
|
||||||
export const authenticationRules = {
|
export const authenticationRules = {
|
||||||
|
@@ -1,11 +1,8 @@
|
|||||||
import { describe, it, expect, vi } from 'vitest';
|
import { describe, it, expect } from 'vitest';
|
||||||
import { allow } from 'graphql-shield';
|
import { allow } from 'graphql-shield';
|
||||||
import jwt from 'jsonwebtoken';
|
|
||||||
import User from '../models/user.js';
|
|
||||||
import { isAuthenticated, authenticationRules } from './authentication.js';
|
import { isAuthenticated, authenticationRules } from './authentication.js';
|
||||||
|
import { createUser } from '../../test/factories/user.js';
|
||||||
vi.mock('jsonwebtoken');
|
import createAuthTokenByUserId from '../helpers/create-auth-token-by-user-id.js';
|
||||||
vi.mock('../models/user.js');
|
|
||||||
|
|
||||||
describe('isAuthenticated', () => {
|
describe('isAuthenticated', () => {
|
||||||
it('should return false if no token is provided', async () => {
|
it('should return false if no token is provided', async () => {
|
||||||
@@ -14,29 +11,26 @@ describe('isAuthenticated', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return false if token is invalid', async () => {
|
it('should return false if token is invalid', async () => {
|
||||||
jwt.verify.mockImplementation(() => {
|
|
||||||
throw new Error('invalid token');
|
|
||||||
});
|
|
||||||
|
|
||||||
const req = { headers: { authorization: 'invalidToken' } };
|
const req = { headers: { authorization: 'invalidToken' } };
|
||||||
expect(await isAuthenticated(null, null, req)).toBe(false);
|
expect(await isAuthenticated(null, null, req)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true if token is valid', async () => {
|
it('should return true if token is valid and there is a user', async () => {
|
||||||
jwt.verify.mockReturnValue({ userId: '123' });
|
const user = await createUser();
|
||||||
|
const token = createAuthTokenByUserId(user.id);
|
||||||
|
|
||||||
User.query.mockReturnValue({
|
const req = { headers: { authorization: token } };
|
||||||
findById: vi.fn().mockReturnValue({
|
|
||||||
leftJoinRelated: vi.fn().mockReturnThis(),
|
|
||||||
withGraphFetched: vi
|
|
||||||
.fn()
|
|
||||||
.mockResolvedValue({ id: '123', role: {}, permissions: {} }),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const req = { headers: { authorization: 'validToken' } };
|
|
||||||
expect(await isAuthenticated(null, null, req)).toBe(true);
|
expect(await isAuthenticated(null, null, req)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return false if token is valid and but there is no user', async () => {
|
||||||
|
const user = await createUser();
|
||||||
|
const token = createAuthTokenByUserId(user.id);
|
||||||
|
await user.$query().delete();
|
||||||
|
|
||||||
|
const req = { headers: { authorization: token } };
|
||||||
|
expect(await isAuthenticated(null, null, req)).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('authentication rules', () => {
|
describe('authentication rules', () => {
|
||||||
|
22
packages/backend/src/helpers/authorization.js
Normal file
22
packages/backend/src/helpers/authorization.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
const authorizationList = {
|
||||||
|
'/api/v1/users/:userId': {
|
||||||
|
action: 'read',
|
||||||
|
subject: 'User',
|
||||||
|
},
|
||||||
|
'/api/v1/users/': {
|
||||||
|
action: 'read',
|
||||||
|
subject: 'User',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const authorizeUser = async (request, response, next) => {
|
||||||
|
const currentRoute = request.baseUrl + request.route.path;
|
||||||
|
const currentRouteRule = authorizationList[currentRoute];
|
||||||
|
|
||||||
|
try {
|
||||||
|
request.currentUser.can(currentRouteRule.action, currentRouteRule.subject);
|
||||||
|
next();
|
||||||
|
} catch (error) {
|
||||||
|
return response.status(403).end();
|
||||||
|
}
|
||||||
|
};
|
11
packages/backend/src/helpers/check-is-cloud.js
Normal file
11
packages/backend/src/helpers/check-is-cloud.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import appConfig from '../config/app.js';
|
||||||
|
|
||||||
|
export const checkIsCloud = async (request, response, next) => {
|
||||||
|
if (appConfig.isCloud) {
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
return response.status(404).end();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default checkIsCloud;
|
25
packages/backend/src/helpers/pagination-rest.js
Normal file
25
packages/backend/src/helpers/pagination-rest.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
const paginateRest = async (query, page) => {
|
||||||
|
const pageSize = 10;
|
||||||
|
|
||||||
|
page = parseInt(page, 10);
|
||||||
|
|
||||||
|
if (isNaN(page) || page < 1) {
|
||||||
|
page = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [records, count] = await Promise.all([
|
||||||
|
query.limit(pageSize).offset((page - 1) * pageSize),
|
||||||
|
query.resultSize(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
pageInfo: {
|
||||||
|
currentPage: page,
|
||||||
|
totalPages: Math.ceil(count / pageSize),
|
||||||
|
},
|
||||||
|
totalCount: count,
|
||||||
|
records,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default paginateRest;
|
42
packages/backend/src/helpers/renderer.js
Normal file
42
packages/backend/src/helpers/renderer.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import serializers from '../serializers/index.js';
|
||||||
|
|
||||||
|
const isPaginated = (object) =>
|
||||||
|
object?.pageInfo &&
|
||||||
|
object?.totalCount !== undefined &&
|
||||||
|
Array.isArray(object?.records);
|
||||||
|
|
||||||
|
const isArray = (object) =>
|
||||||
|
Array.isArray(object) || Array.isArray(object?.records);
|
||||||
|
|
||||||
|
const totalCount = (object) =>
|
||||||
|
isPaginated(object) ? object.totalCount : isArray(object) ? object.length : 1;
|
||||||
|
|
||||||
|
const renderObject = (response, object) => {
|
||||||
|
let data = isPaginated(object) ? object.records : object;
|
||||||
|
const type = isPaginated(object)
|
||||||
|
? object.records[0].constructor.name
|
||||||
|
: object.constructor.name;
|
||||||
|
|
||||||
|
const serializer = serializers[type];
|
||||||
|
|
||||||
|
if (serializer) {
|
||||||
|
data = Array.isArray(data)
|
||||||
|
? data.map((item) => serializer(item))
|
||||||
|
: serializer(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
const computedPayload = {
|
||||||
|
data,
|
||||||
|
meta: {
|
||||||
|
type,
|
||||||
|
count: totalCount(object),
|
||||||
|
isArray: isArray(object),
|
||||||
|
currentPage: isPaginated(object) ? object.pageInfo.currentPage : null,
|
||||||
|
totalPages: isPaginated(object) ? object.pageInfo.totalPages : null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return response.json(computedPayload);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { renderObject };
|
8
packages/backend/src/routes/api/v1/automatisch.js
Normal file
8
packages/backend/src/routes/api/v1/automatisch.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { Router } from 'express';
|
||||||
|
import versionAction from '../../../controllers/api/v1/automatisch/version.js';
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get('/version', versionAction);
|
||||||
|
|
||||||
|
export default router;
|
22
packages/backend/src/routes/api/v1/users.js
Normal file
22
packages/backend/src/routes/api/v1/users.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { Router } from 'express';
|
||||||
|
import { authenticateUser } from '../../../helpers/authentication.js';
|
||||||
|
import { authorizeUser } from '../../../helpers/authorization.js';
|
||||||
|
import checkIsCloud from '../../../helpers/check-is-cloud.js';
|
||||||
|
import getCurrentUserAction from '../../../controllers/api/v1/users/get-current-user.js';
|
||||||
|
import getUserAction from '../../../controllers/api/v1/users/get-user.js';
|
||||||
|
import getUsersAction from '../../../controllers/api/v1/users/get-users.js';
|
||||||
|
import getUserTrialAction from '../../../controllers/api/v1/users/get-user-trial.ee.js';
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get('/', authenticateUser, authorizeUser, getUsersAction);
|
||||||
|
router.get('/me', authenticateUser, getCurrentUserAction);
|
||||||
|
router.get('/:userId', authenticateUser, authorizeUser, getUserAction);
|
||||||
|
router.get(
|
||||||
|
'/:userId/trial',
|
||||||
|
authenticateUser,
|
||||||
|
checkIsCloud,
|
||||||
|
getUserTrialAction
|
||||||
|
);
|
||||||
|
|
||||||
|
export default router;
|
8
packages/backend/src/routes/healthcheck.js
Normal file
8
packages/backend/src/routes/healthcheck.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { Router } from 'express';
|
||||||
|
import indexAction from '../controllers/healthcheck/index.js';
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get('/', indexAction);
|
||||||
|
|
||||||
|
export default router;
|
@@ -2,11 +2,17 @@ import { Router } from 'express';
|
|||||||
import graphQLInstance from '../helpers/graphql-instance.js';
|
import graphQLInstance from '../helpers/graphql-instance.js';
|
||||||
import webhooksRouter from './webhooks.js';
|
import webhooksRouter from './webhooks.js';
|
||||||
import paddleRouter from './paddle.ee.js';
|
import paddleRouter from './paddle.ee.js';
|
||||||
|
import healthcheckRouter from './healthcheck.js';
|
||||||
|
import automatischRouter from './api/v1/automatisch.js';
|
||||||
|
import usersRouter from './api/v1/users.js';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.use('/graphql', graphQLInstance);
|
router.use('/graphql', graphQLInstance);
|
||||||
router.use('/webhooks', webhooksRouter);
|
router.use('/webhooks', webhooksRouter);
|
||||||
router.use('/paddle', paddleRouter);
|
router.use('/paddle', paddleRouter);
|
||||||
|
router.use('/healthcheck', healthcheckRouter);
|
||||||
|
router.use('/api/v1/automatisch', automatischRouter);
|
||||||
|
router.use('/api/v1/users', usersRouter);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
11
packages/backend/src/serializers/index.js
Normal file
11
packages/backend/src/serializers/index.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import userSerializer from './user.js';
|
||||||
|
import roleSerializer from './role.js';
|
||||||
|
import permissionSerializer from './permission.js';
|
||||||
|
|
||||||
|
const serializers = {
|
||||||
|
User: userSerializer,
|
||||||
|
Role: roleSerializer,
|
||||||
|
Permission: permissionSerializer,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default serializers;
|
13
packages/backend/src/serializers/permission.js
Normal file
13
packages/backend/src/serializers/permission.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
const permissionSerializer = (permission) => {
|
||||||
|
return {
|
||||||
|
id: permission.id,
|
||||||
|
roleId: permission.roleId,
|
||||||
|
action: permission.action,
|
||||||
|
subject: permission.subject,
|
||||||
|
conditions: permission.conditions,
|
||||||
|
createdAt: permission.createdAt,
|
||||||
|
updatedAt: permission.updatedAt,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default permissionSerializer;
|
25
packages/backend/src/serializers/permission.test.js
Normal file
25
packages/backend/src/serializers/permission.test.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import { createPermission } from '../../test/factories/permission';
|
||||||
|
import permissionSerializer from './permission';
|
||||||
|
|
||||||
|
describe('permissionSerializer', () => {
|
||||||
|
let permission;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
permission = await createPermission();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return permission data', async () => {
|
||||||
|
const expectedPayload = {
|
||||||
|
id: permission.id,
|
||||||
|
roleId: permission.roleId,
|
||||||
|
action: permission.action,
|
||||||
|
subject: permission.subject,
|
||||||
|
conditions: permission.conditions,
|
||||||
|
createdAt: permission.createdAt,
|
||||||
|
updatedAt: permission.updatedAt,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(permissionSerializer(permission)).toEqual(expectedPayload);
|
||||||
|
});
|
||||||
|
});
|
13
packages/backend/src/serializers/role.js
Normal file
13
packages/backend/src/serializers/role.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
const roleSerializer = (role) => {
|
||||||
|
return {
|
||||||
|
id: role.id,
|
||||||
|
name: role.name,
|
||||||
|
key: role.key,
|
||||||
|
description: role.description,
|
||||||
|
createdAt: role.createdAt,
|
||||||
|
updatedAt: role.updatedAt,
|
||||||
|
isAdmin: role.isAdmin,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default roleSerializer;
|
25
packages/backend/src/serializers/role.test.js
Normal file
25
packages/backend/src/serializers/role.test.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import { createRole } from '../../test/factories/role';
|
||||||
|
import roleSerializer from './role';
|
||||||
|
|
||||||
|
describe('roleSerializer', () => {
|
||||||
|
let role;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
role = await createRole();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return role data', async () => {
|
||||||
|
const expectedPayload = {
|
||||||
|
id: role.id,
|
||||||
|
name: role.name,
|
||||||
|
key: role.key,
|
||||||
|
description: role.description,
|
||||||
|
createdAt: role.createdAt,
|
||||||
|
updatedAt: role.updatedAt,
|
||||||
|
isAdmin: role.isAdmin,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(roleSerializer(role)).toEqual(expectedPayload);
|
||||||
|
});
|
||||||
|
});
|
32
packages/backend/src/serializers/user.js
Normal file
32
packages/backend/src/serializers/user.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import roleSerializer from './role.js';
|
||||||
|
import permissionSerializer from './permission.js';
|
||||||
|
import appConfig from '../config/app.js';
|
||||||
|
|
||||||
|
const userSerializer = (user) => {
|
||||||
|
let userData = {
|
||||||
|
id: user.id,
|
||||||
|
email: user.email,
|
||||||
|
createdAt: user.createdAt,
|
||||||
|
updatedAt: user.updatedAt,
|
||||||
|
fullName: user.fullName,
|
||||||
|
roleId: user.roleId,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (user.role) {
|
||||||
|
userData.role = roleSerializer(user.role);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.permissions) {
|
||||||
|
userData.permissions = user.permissions.map((permission) =>
|
||||||
|
permissionSerializer(permission)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (appConfig.isCloud && user.trialExpiryDate) {
|
||||||
|
userData.trialExpiryDate = user.trialExpiryDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
return userData;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default userSerializer;
|
76
packages/backend/src/serializers/user.test.js
Normal file
76
packages/backend/src/serializers/user.test.js
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import appConfig from '../config/app';
|
||||||
|
import { createUser } from '../../test/factories/user';
|
||||||
|
import { createPermission } from '../../test/factories/permission';
|
||||||
|
import userSerializer from './user';
|
||||||
|
|
||||||
|
describe('userSerializer', () => {
|
||||||
|
let user, role, permissionOne, permissionTwo;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await createUser();
|
||||||
|
role = await user.$relatedQuery('role');
|
||||||
|
|
||||||
|
permissionOne = await createPermission({
|
||||||
|
roleId: role.id,
|
||||||
|
action: 'read',
|
||||||
|
subject: 'User',
|
||||||
|
});
|
||||||
|
|
||||||
|
permissionTwo = await createPermission({
|
||||||
|
roleId: role.id,
|
||||||
|
action: 'read',
|
||||||
|
subject: 'Role',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return user data', async () => {
|
||||||
|
vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(false);
|
||||||
|
|
||||||
|
const expectedPayload = {
|
||||||
|
createdAt: user.createdAt,
|
||||||
|
email: user.email,
|
||||||
|
fullName: user.fullName,
|
||||||
|
id: user.id,
|
||||||
|
roleId: user.roleId,
|
||||||
|
updatedAt: user.updatedAt,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(userSerializer(user)).toEqual(expectedPayload);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return user data with the role', async () => {
|
||||||
|
user.role = role;
|
||||||
|
|
||||||
|
const expectedPayload = {
|
||||||
|
role,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(userSerializer(user)).toMatchObject(expectedPayload);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return user data with the permissions', async () => {
|
||||||
|
user.permissions = [permissionOne, permissionTwo];
|
||||||
|
|
||||||
|
const expectedPayload = {
|
||||||
|
permissions: [permissionOne, permissionTwo],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(userSerializer(user)).toMatchObject(expectedPayload);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return user data with trial expiry date', async () => {
|
||||||
|
vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(true);
|
||||||
|
|
||||||
|
await user.$query().patch({
|
||||||
|
trialExpiryDate: DateTime.now().plus({ days: 30 }).toISODate(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const expectedPayload = {
|
||||||
|
trialExpiryDate: user.trialExpiryDate,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(userSerializer(user)).toMatchObject(expectedPayload);
|
||||||
|
});
|
||||||
|
});
|
@@ -1,4 +1,5 @@
|
|||||||
import { faker } from '@faker-js/faker';
|
import { faker } from '@faker-js/faker';
|
||||||
|
import Config from '../../src/models/config';
|
||||||
|
|
||||||
export const createConfig = async (params = {}) => {
|
export const createConfig = async (params = {}) => {
|
||||||
const configData = {
|
const configData = {
|
||||||
@@ -6,10 +7,7 @@ export const createConfig = async (params = {}) => {
|
|||||||
value: params?.value || { data: 'sampleConfig' },
|
value: params?.value || { data: 'sampleConfig' },
|
||||||
};
|
};
|
||||||
|
|
||||||
const [config] = await global.knex
|
const config = await Config.query().insert(configData).returning('*');
|
||||||
.table('config')
|
|
||||||
.insert(configData)
|
|
||||||
.returning('*');
|
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
};
|
};
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import appConfig from '../../src/config/app';
|
import appConfig from '../../src/config/app';
|
||||||
import { AES } from 'crypto-js';
|
import { AES } from 'crypto-js';
|
||||||
|
import Connection from '../../src/models/connection';
|
||||||
|
|
||||||
export const createConnection = async (params = {}) => {
|
export const createConnection = async (params = {}) => {
|
||||||
params.key = params?.key || 'deepl';
|
params.key = params?.key || 'deepl';
|
||||||
@@ -16,10 +17,7 @@ export const createConnection = async (params = {}) => {
|
|||||||
appConfig.encryptionKey
|
appConfig.encryptionKey
|
||||||
).toString();
|
).toString();
|
||||||
|
|
||||||
const [connection] = await global.knex
|
const connection = await Connection.query().insert(params).returning('*');
|
||||||
.table('connections')
|
|
||||||
.insert(params)
|
|
||||||
.returning('*');
|
|
||||||
|
|
||||||
return connection;
|
return connection;
|
||||||
};
|
};
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import ExecutionStep from '../../src/models/execution-step';
|
||||||
import { createExecution } from './execution';
|
import { createExecution } from './execution';
|
||||||
import { createStep } from './step';
|
import { createStep } from './step';
|
||||||
|
|
||||||
@@ -8,8 +9,7 @@ export const createExecutionStep = async (params = {}) => {
|
|||||||
params.dataIn = params?.dataIn || { dataIn: 'dataIn' };
|
params.dataIn = params?.dataIn || { dataIn: 'dataIn' };
|
||||||
params.dataOut = params?.dataOut || { dataOut: 'dataOut' };
|
params.dataOut = params?.dataOut || { dataOut: 'dataOut' };
|
||||||
|
|
||||||
const [executionStep] = await global.knex
|
const executionStep = await ExecutionStep.query()
|
||||||
.table('executionSteps')
|
|
||||||
.insert(params)
|
.insert(params)
|
||||||
.returning('*');
|
.returning('*');
|
||||||
|
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import Execution from '../../src/models/execution';
|
||||||
import { createFlow } from './flow';
|
import { createFlow } from './flow';
|
||||||
|
|
||||||
export const createExecution = async (params = {}) => {
|
export const createExecution = async (params = {}) => {
|
||||||
@@ -6,10 +7,7 @@ export const createExecution = async (params = {}) => {
|
|||||||
params.createdAt = params?.createdAt || new Date().toISOString();
|
params.createdAt = params?.createdAt || new Date().toISOString();
|
||||||
params.updatedAt = params?.updatedAt || new Date().toISOString();
|
params.updatedAt = params?.updatedAt || new Date().toISOString();
|
||||||
|
|
||||||
const [execution] = await global.knex
|
const execution = await Execution.query().insert(params).returning('*');
|
||||||
.table('executions')
|
|
||||||
.insert(params)
|
|
||||||
.returning('*');
|
|
||||||
|
|
||||||
return execution;
|
return execution;
|
||||||
};
|
};
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import Flow from '../../src/models/flow';
|
||||||
import { createUser } from './user';
|
import { createUser } from './user';
|
||||||
|
|
||||||
export const createFlow = async (params = {}) => {
|
export const createFlow = async (params = {}) => {
|
||||||
@@ -6,7 +7,7 @@ export const createFlow = async (params = {}) => {
|
|||||||
params.createdAt = params?.createdAt || new Date().toISOString();
|
params.createdAt = params?.createdAt || new Date().toISOString();
|
||||||
params.updatedAt = params?.updatedAt || new Date().toISOString();
|
params.updatedAt = params?.updatedAt || new Date().toISOString();
|
||||||
|
|
||||||
const [flow] = await global.knex.table('flows').insert(params).returning('*');
|
const flow = await Flow.query().insert(params).returning('*');
|
||||||
|
|
||||||
return flow;
|
return flow;
|
||||||
};
|
};
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import Permission from '../../src/models/permission';
|
||||||
import { createRole } from './role';
|
import { createRole } from './role';
|
||||||
|
|
||||||
export const createPermission = async (params = {}) => {
|
export const createPermission = async (params = {}) => {
|
||||||
@@ -6,10 +7,7 @@ export const createPermission = async (params = {}) => {
|
|||||||
params.subject = params?.subject || 'User';
|
params.subject = params?.subject || 'User';
|
||||||
params.conditions = params?.conditions || ['isCreator'];
|
params.conditions = params?.conditions || ['isCreator'];
|
||||||
|
|
||||||
const [permission] = await global.knex
|
const permission = await Permission.query().insert(params).returning('*');
|
||||||
.table('permissions')
|
|
||||||
.insert(params)
|
|
||||||
.returning('*');
|
|
||||||
|
|
||||||
return permission;
|
return permission;
|
||||||
};
|
};
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
|
import Role from '../../src/models/role';
|
||||||
|
|
||||||
export const createRole = async (params = {}) => {
|
export const createRole = async (params = {}) => {
|
||||||
params.name = params?.name || 'Viewer';
|
params.name = params?.name || 'Viewer';
|
||||||
params.key = params?.key || 'viewer';
|
params.key = params?.key || 'viewer';
|
||||||
|
|
||||||
const [role] = await global.knex.table('roles').insert(params).returning('*');
|
const role = await Role.query().insert(params).returning('*');
|
||||||
|
|
||||||
return role;
|
return role;
|
||||||
};
|
};
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import Step from '../../src/models/step';
|
||||||
import { createFlow } from './flow';
|
import { createFlow } from './flow';
|
||||||
|
|
||||||
export const createStep = async (params = {}) => {
|
export const createStep = async (params = {}) => {
|
||||||
@@ -16,7 +17,7 @@ export const createStep = async (params = {}) => {
|
|||||||
params.appKey =
|
params.appKey =
|
||||||
params?.appKey || (params.type === 'action' ? 'deepl' : 'webhook');
|
params?.appKey || (params.type === 'action' ? 'deepl' : 'webhook');
|
||||||
|
|
||||||
const [step] = await global.knex.table('steps').insert(params).returning('*');
|
const step = await Step.query().insert(params).returning('*');
|
||||||
|
|
||||||
return step;
|
return step;
|
||||||
};
|
};
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { createRole } from './role';
|
import { createRole } from './role';
|
||||||
import { faker } from '@faker-js/faker';
|
import { faker } from '@faker-js/faker';
|
||||||
|
import User from '../../src/models/user';
|
||||||
|
|
||||||
export const createUser = async (params = {}) => {
|
export const createUser = async (params = {}) => {
|
||||||
params.roleId = params?.roleId || (await createRole()).id;
|
params.roleId = params?.roleId || (await createRole()).id;
|
||||||
@@ -7,7 +8,7 @@ export const createUser = async (params = {}) => {
|
|||||||
params.email = params?.email || faker.internet.email();
|
params.email = params?.email || faker.internet.email();
|
||||||
params.password = params?.password || faker.internet.password();
|
params.password = params?.password || faker.internet.password();
|
||||||
|
|
||||||
const [user] = await global.knex.table('users').insert(params).returning('*');
|
const user = await User.query().insert(params).returning('*');
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
};
|
};
|
||||||
|
@@ -0,0 +1,32 @@
|
|||||||
|
const getCurrentUserMock = (currentUser, role) => {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
createdAt: currentUser.createdAt.toISOString(),
|
||||||
|
email: currentUser.email,
|
||||||
|
fullName: currentUser.fullName,
|
||||||
|
id: currentUser.id,
|
||||||
|
permissions: [],
|
||||||
|
role: {
|
||||||
|
createdAt: role.createdAt.toISOString(),
|
||||||
|
description: null,
|
||||||
|
id: role.id,
|
||||||
|
isAdmin: role.isAdmin,
|
||||||
|
key: role.key,
|
||||||
|
name: role.name,
|
||||||
|
updatedAt: role.updatedAt.toISOString(),
|
||||||
|
},
|
||||||
|
roleId: role.id,
|
||||||
|
trialExpiryDate: currentUser.trialExpiryDate.toISOString(),
|
||||||
|
updatedAt: currentUser.updatedAt.toISOString(),
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
count: 1,
|
||||||
|
currentPage: null,
|
||||||
|
isArray: false,
|
||||||
|
totalPages: null,
|
||||||
|
type: 'User',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getCurrentUserMock;
|
@@ -0,0 +1,17 @@
|
|||||||
|
const getUserTrialMock = async (currentUser) => {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
inTrial: await currentUser.inTrial(),
|
||||||
|
expireAt: currentUser.trialExpiryDate.toISOString(),
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
count: 1,
|
||||||
|
currentPage: null,
|
||||||
|
isArray: false,
|
||||||
|
totalPages: null,
|
||||||
|
type: 'Object',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getUserTrialMock;
|
31
packages/backend/test/mocks/rest/api/v1/users/get-user.js
Normal file
31
packages/backend/test/mocks/rest/api/v1/users/get-user.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
const getUserMock = (currentUser, role) => {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
createdAt: currentUser.createdAt.toISOString(),
|
||||||
|
email: currentUser.email,
|
||||||
|
fullName: currentUser.fullName,
|
||||||
|
id: currentUser.id,
|
||||||
|
role: {
|
||||||
|
createdAt: role.createdAt.toISOString(),
|
||||||
|
description: null,
|
||||||
|
id: role.id,
|
||||||
|
isAdmin: role.isAdmin,
|
||||||
|
key: role.key,
|
||||||
|
name: role.name,
|
||||||
|
updatedAt: role.updatedAt.toISOString(),
|
||||||
|
},
|
||||||
|
roleId: role.id,
|
||||||
|
trialExpiryDate: currentUser.trialExpiryDate.toISOString(),
|
||||||
|
updatedAt: currentUser.updatedAt.toISOString(),
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
count: 1,
|
||||||
|
currentPage: null,
|
||||||
|
isArray: false,
|
||||||
|
totalPages: null,
|
||||||
|
type: 'User',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getUserMock;
|
38
packages/backend/test/mocks/rest/api/v1/users/get-users.js
Normal file
38
packages/backend/test/mocks/rest/api/v1/users/get-users.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
const getUsersMock = async (users, roles) => {
|
||||||
|
const data = users.map((user) => {
|
||||||
|
const role = roles.find((r) => r.id === user.roleId);
|
||||||
|
return {
|
||||||
|
createdAt: user.createdAt.toISOString(),
|
||||||
|
email: user.email,
|
||||||
|
fullName: user.fullName,
|
||||||
|
id: user.id,
|
||||||
|
role: role
|
||||||
|
? {
|
||||||
|
createdAt: role.createdAt.toISOString(),
|
||||||
|
description: role.description,
|
||||||
|
id: role.id,
|
||||||
|
isAdmin: role.isAdmin,
|
||||||
|
key: role.key,
|
||||||
|
name: role.name,
|
||||||
|
updatedAt: role.updatedAt.toISOString(),
|
||||||
|
}
|
||||||
|
: null, // Fallback to null if role not found
|
||||||
|
roleId: user.roleId,
|
||||||
|
trialExpiryDate: user.trialExpiryDate.toISOString(),
|
||||||
|
updatedAt: user.updatedAt.toISOString(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: data,
|
||||||
|
meta: {
|
||||||
|
count: data.length,
|
||||||
|
currentPage: 1,
|
||||||
|
isArray: true,
|
||||||
|
totalPages: 1,
|
||||||
|
type: 'User',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getUsersMock;
|
@@ -32,6 +32,16 @@ export default defineConfig({
|
|||||||
],
|
],
|
||||||
sidebar: {
|
sidebar: {
|
||||||
'/apps/': [
|
'/apps/': [
|
||||||
|
{
|
||||||
|
text: 'Bigin By Zoho CRM',
|
||||||
|
collapsible: true,
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{ text: 'Triggers', link: '/apps/bigin-by-zoho-crm/triggers' },
|
||||||
|
{ text: 'Actions', link: '/apps/bigin-by-zoho-crm/actions' },
|
||||||
|
{ text: 'Connection', link: '/apps/bigin-by-zoho-crm/connection' },
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
text: 'Carbone',
|
text: 'Carbone',
|
||||||
collapsible: true,
|
collapsible: true,
|
||||||
@@ -305,7 +315,7 @@ export default defineConfig({
|
|||||||
collapsed: true,
|
collapsed: true,
|
||||||
items: [
|
items: [
|
||||||
{ text: 'Actions', link: '/apps/removebg/actions' },
|
{ text: 'Actions', link: '/apps/removebg/actions' },
|
||||||
{ text: 'Connection', link: '/apps/removebg/connection' }
|
{ text: 'Connection', link: '/apps/removebg/connection' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
16
packages/docs/pages/apps/bigin-by-zoho-crm/actions.md
Normal file
16
packages/docs/pages/apps/bigin-by-zoho-crm/actions.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
favicon: /favicons/bigin-by-zoho-crm.svg
|
||||||
|
items:
|
||||||
|
- name: Create company
|
||||||
|
desc: Creates a new company.
|
||||||
|
- name: Create contact
|
||||||
|
desc: Creates a new contact.
|
||||||
|
- name: Create event
|
||||||
|
desc: Creates a new event.
|
||||||
|
---
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import CustomListing from '../../components/CustomListing.vue'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CustomListing />
|
18
packages/docs/pages/apps/bigin-by-zoho-crm/connection.md
Normal file
18
packages/docs/pages/apps/bigin-by-zoho-crm/connection.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Bigin By Zoho CRM
|
||||||
|
|
||||||
|
:::info
|
||||||
|
This page explains the steps you need to follow to set up the Bigin By Zoho CRM
|
||||||
|
connection in Automatisch. If any of the steps are outdated, please let us know!
|
||||||
|
:::
|
||||||
|
|
||||||
|
1. Go to the [Zoho API Console](https://api-console.zoho.com) to register an application.
|
||||||
|
2. Login to your account.
|
||||||
|
3. Click on the **GET STARTED** button.
|
||||||
|
4. Choose the **Server-based Applications** client type.
|
||||||
|
5. Fill the **Create New Client** form.
|
||||||
|
6. Copy **OAuth Redirect URL** from Automatisch to **Authorized redirect URIs** field, and click on the **Create** button.
|
||||||
|
7. Copy the **Client ID** value to the `Client ID` field on Automatisch.
|
||||||
|
8. Copy the **Client Secret** value to the `Client Secret` field on Automatisch.
|
||||||
|
9. Select the region appropriate for your account.
|
||||||
|
10. Click **Submit** button on Automatisch.
|
||||||
|
11. Congrats! Start using your new Bigin By Zoho CRM connection within the flows.
|
20
packages/docs/pages/apps/bigin-by-zoho-crm/triggers.md
Normal file
20
packages/docs/pages/apps/bigin-by-zoho-crm/triggers.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
favicon: /favicons/bigin-by-zoho-crm.svg
|
||||||
|
items:
|
||||||
|
- name: New calls
|
||||||
|
desc: Triggers when a new call is added.
|
||||||
|
- name: New companies
|
||||||
|
desc: Triggers when a new company is created.
|
||||||
|
- name: New contacts
|
||||||
|
desc: Triggers when a new contact is created.
|
||||||
|
- name: New products
|
||||||
|
desc: Triggers when a new product is created.
|
||||||
|
- name: New tasks
|
||||||
|
desc: Triggers when a new task is created.
|
||||||
|
---
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import CustomListing from '../../components/CustomListing.vue'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CustomListing />
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
The following integrations are currently supported by Automatisch.
|
The following integrations are currently supported by Automatisch.
|
||||||
|
|
||||||
|
- [Bigin By Zoho CRM](/apps/bigin-by-zoho-crm/triggers)
|
||||||
- [Carbone](/apps/carbone/actions)
|
- [Carbone](/apps/carbone/actions)
|
||||||
- [DeepL](/apps/deepl/actions)
|
- [DeepL](/apps/deepl/actions)
|
||||||
- [Delay](/apps/delay/actions)
|
- [Delay](/apps/delay/actions)
|
||||||
|
32
packages/docs/pages/public/favicons/bigin-by-zoho-crm.svg
Normal file
32
packages/docs/pages/public/favicons/bigin-by-zoho-crm.svg
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" id="b" data-name="Layer 2" width="327.714" height="120" viewBox="0 0 327.714 120">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.d {
|
||||||
|
fill: #039649;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<g id="c" data-name="Layer 1">
|
||||||
|
<g>
|
||||||
|
<path class="d" d="m52.39,120c-1.801,0-3.6-.6-5.101-1.5-2.1-1.5-3.6-4.2-3.6-6.9v-24.6L1.09,12.901C-.41,9.9-.41,6.601,1.391,4.2,2.891,1.5,5.89,0,8.89,0h78c2.999,0,5.698,1.5,7.199,4.2,1.801,2.702,1.801,6.3.301,9.002l-5.101,9h10.201c2.999,0,5.698,1.5,7.199,4.2,1.801,2.7,1.801,6.3.301,9l-29.701,51.3v18.599c0,3.899-2.399,6.9-5.999,8.099l-16.2,6.3c-.6.301-1.799.301-2.7.301M8.89,8.4q-.301.301-.301.602l42.301,73.798c1.199,2.1,1.199,3.299,1.199,4.501v23.998s.301.301.6,0l16.2-6.3h.6v-17.999c0-1.199,0-3.299,1.201-4.8l29.4-50.999c0-.301,0-.6-.301-.6l-53.699-.301,14.399,24.901,10.201-17.7c1.199-2.1,3.6-2.7,5.7-1.5,2.1,1.199,2.7,3.6,1.498,5.7l-11.998,20.699c-2.702,4.2-8.701,3.901-11.102.301l-17.4-30.9c-1.199-1.799-1.199-4.2,0-6.3,1.201-2.1,3.301-3.6,5.7-3.6h36.6l7.499-12.899s0-.301-.299-.602H8.89Z"/>
|
||||||
|
<g>
|
||||||
|
<path d="m181.725,48.737c0,3.089-.54,5.684-1.618,7.788-1.081,2.104-2.548,3.79-4.407,5.062-1.858,1.27-4.016,2.179-6.476,2.726-2.459.547-5.069.819-7.828.819h-24.591V6.521h23.034c2.676,0,5.198.24,7.561.718,2.364.479,4.427,1.311,6.189,2.5,1.762,1.189,3.149,2.787,4.16,4.795,1.011,2.008,1.517,4.53,1.517,7.562,0,3.17-.786,5.813-2.357,7.93-1.57,2.119-3.913,3.628-7.028,4.53,4.017.819,6.995,2.371,8.935,4.652s2.91,5.458,2.91,9.529Zm-33.813-17.624h10.533c1.612,0,3.033-.136,4.263-.41,1.23-.272,2.268-.738,3.115-1.393.846-.656,1.489-1.55,1.927-2.684.436-1.134.655-2.562.655-4.284,0-1.612-.267-2.923-.799-3.934-.532-1.01-1.25-1.809-2.152-2.398-.902-.587-1.967-.99-3.197-1.209s-2.542-.328-3.934-.328h-10.411v16.64Zm0,26.067h12.09c1.64,0,3.115-.169,4.427-.512,1.311-.342,2.424-.894,3.341-1.66.915-.764,1.612-1.748,2.091-2.951.478-1.202.716-2.663.716-4.385,0-1.802-.294-3.285-.881-4.447-.588-1.161-1.394-2.083-2.419-2.766s-2.227-1.154-3.606-1.414c-1.381-.26-2.863-.39-4.447-.39h-11.312v18.525Z"/>
|
||||||
|
<path d="m202.257,9.555c0,.847-.151,1.633-.451,2.356-.3.724-.716,1.34-1.25,1.844-.532.507-1.167.902-1.905,1.189s-1.53.431-2.377.431c-.819,0-1.598-.144-2.336-.431s-1.388-.69-1.947-1.209c-.56-.519-.998-1.133-1.311-1.844-.315-.711-.471-1.489-.471-2.336s.156-1.626.471-2.336c.314-.711.744-1.325,1.291-1.845.546-.519,1.187-.922,1.925-1.209s1.53-.431,2.377-.431c.821,0,1.598.144,2.336.431s1.373.69,1.907,1.209c.532.52.955,1.134,1.27,1.845.314.71.471,1.489.471,2.336Zm-.778,12.705v42.871h-10.246V22.261h10.246Z"/>
|
||||||
|
<path d="m254.186,61.935c0,3.361-.587,6.236-1.762,8.627-1.174,2.391-2.78,4.352-4.815,5.882-2.036,1.529-4.407,2.65-7.111,3.361-2.706.71-5.589,1.065-8.648,1.065-2.023,0-4.037-.157-6.045-.471-2.009-.314-3.908-.861-5.697-1.64-1.79-.778-3.395-1.837-4.816-3.175-1.421-1.34-2.555-3.021-3.402-5.042l8.074-3.525c.519,1.202,1.202,2.193,2.049,2.971.847.779,1.797,1.408,2.848,1.885,1.051.479,2.172.814,3.361,1.005s2.398.287,3.628.287c1.885,0,3.572-.232,5.062-.696,1.489-.466,2.752-1.169,3.79-2.111,1.039-.943,1.838-2.132,2.399-3.566.559-1.434.839-3.107.839-5.02v-6.393c-.71,1.229-1.592,2.336-2.643,3.319-1.053.983-2.207,1.824-3.464,2.52s-2.582,1.237-3.976,1.62c-1.393.383-2.814.574-4.263.574-3.033,0-5.737-.56-8.114-1.681-2.377-1.119-4.379-2.636-6.005-4.55-1.625-1.912-2.862-4.139-3.709-6.68-.847-2.542-1.27-5.233-1.27-8.074,0-2.814.451-5.491,1.353-8.033.901-2.542,2.192-4.775,3.873-6.702,1.68-1.927,3.709-3.464,6.086-4.611,2.376-1.147,5.054-1.721,8.033-1.721,2.923,0,5.621.594,8.094,1.782,2.472,1.189,4.473,3.082,6.004,5.677v-6.557h10.246v39.674Zm-33.198-19.017c0,1.666.252,3.265.758,4.795s1.237,2.876,2.193,4.037c.957,1.162,2.137,2.084,3.545,2.767s3.013,1.025,4.816,1.025c2.021,0,3.784-.355,5.287-1.066,1.502-.711,2.746-1.673,3.729-2.89.985-1.215,1.722-2.643,2.213-4.283.492-1.64.738-3.374.738-5.206,0-1.802-.239-3.49-.716-5.062-.479-1.57-1.203-2.93-2.173-4.077s-2.179-2.056-3.626-2.726c-1.449-.67-3.157-1.005-5.123-1.005-2.049,0-3.812.363-5.287,1.086-1.476.724-2.684,1.709-3.628,2.951-.943,1.243-1.633,2.699-2.069,4.365-.438,1.666-.656,3.429-.656,5.287Z"/>
|
||||||
|
<path d="m277.096,9.555c0,.847-.151,1.633-.451,2.356-.3.724-.716,1.34-1.25,1.844-.532.507-1.167.902-1.905,1.189s-1.53.431-2.377.431c-.819,0-1.598-.144-2.336-.431s-1.388-.69-1.947-1.209c-.56-.519-.998-1.133-1.311-1.844-.315-.711-.471-1.489-.471-2.336s.156-1.626.471-2.336c.314-.711.744-1.325,1.291-1.845.546-.519,1.187-.922,1.925-1.209s1.53-.431,2.377-.431c.821,0,1.598.144,2.336.431s1.373.69,1.907,1.209c.532.52.955,1.134,1.27,1.845.314.71.471,1.489.471,2.336Zm-.778,12.705v42.871h-10.246V22.261h10.246Z"/>
|
||||||
|
<path d="m308.451,29.801c-1.585,0-2.999.24-4.243.718-1.243.479-2.288,1.162-3.135,2.049-.847.889-1.496,1.961-1.947,3.217-.451,1.258-.676,2.651-.676,4.181v25.165h-10.246V22.261h10.246v6.803c.683-1.338,1.537-2.492,2.562-3.462,1.025-.97,2.165-1.769,3.422-2.399,1.257-.627,2.59-1.091,3.997-1.393,1.406-.3,2.82-.451,4.241-.451,2.623,0,4.884.431,6.783,1.291s3.464,2.049,4.694,3.565c1.229,1.517,2.131,3.307,2.704,5.37s.861,4.311.861,6.742v26.805h-10.328v-25.822c0-3.005-.711-5.341-2.132-7.008-1.421-1.666-3.688-2.5-6.803-2.5Z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path d="m152.871,102.12c0,1.138-.168,2.221-.507,3.25-.337,1.028-.828,1.935-1.471,2.72s-1.436,1.41-2.379,1.874-2.021.696-3.234.696c-.579,0-1.155-.061-1.723-.183-.569-.121-1.107-.306-1.613-.553-.505-.247-.974-.561-1.407-.941s-.801-.822-1.107-1.328v2.72h-2.625v-24.446h2.625v10.689c.273-.464.632-.882,1.076-1.257.442-.374.93-.696,1.463-.964.531-.27,1.082-.477,1.652-.626.569-.146,1.122-.22,1.66-.22,1.244,0,2.34.227,3.289.679.95.454,1.743,1.068,2.38,1.843s1.117,1.685,1.44,2.728c.321,1.044.482,2.151.482,3.321Zm-13.536.126c0,.917.123,1.756.371,2.515.249.759.614,1.415,1.1,1.968.485.553,1.079.984,1.787,1.289.706.306,1.517.459,2.435.459.991,0,1.813-.178,2.467-.53.653-.354,1.178-.828,1.573-1.424.395-.595.674-1.283.838-2.063.163-.78.245-1.603.245-2.467,0-.801-.104-1.576-.308-2.325-.206-.748-.52-1.415-.941-1.999-.422-.586-.958-1.055-1.605-1.407-.648-.354-1.415-.53-2.3-.53-.981,0-1.827.174-2.538.521-.711.349-1.3.818-1.764,1.409-.464.59-.806,1.28-1.028,2.071s-.332,1.629-.332,2.514Z"/>
|
||||||
|
<path d="m169.949,93.834l-9.141,22.297h-2.656l3.131-7.464-6.388-14.833h2.814l4.965,11.86,4.586-11.86h2.689Z"/>
|
||||||
|
<path d="m194.11,108.034v2.34h-16.414v-1.139l13.189-19.228h-12.05v-2.246h15.465v1.139l-12.84,19.134h12.65Z"/>
|
||||||
|
<path d="m213.229,102.215c0,1.202-.206,2.319-.617,3.352-.411,1.034-.986,1.927-1.723,2.681-.739.753-1.611,1.344-2.618,1.77-1.007.428-2.106.641-3.296.641-1.256,0-2.388-.222-3.4-.665s-1.874-1.053-2.585-1.834-1.257-1.697-1.637-2.752c-.38-1.053-.57-2.192-.57-3.416,0-1.191.201-2.3.601-3.329.4-1.028.964-1.92,1.692-2.68.727-.759,1.594-1.354,2.601-1.787s2.116-.648,3.329-.648c1.254,0,2.391.22,3.408.663s1.881,1.055,2.593,1.835,1.26,1.697,1.644,2.751c.385,1.055.578,2.192.578,3.416Zm-13.726.031c0,.886.117,1.708.349,2.467s.579,1.418,1.043,1.977c.464.558,1.038.995,1.723,1.311.685.317,1.481.476,2.388.476.938,0,1.753-.175,2.443-.522.691-.349,1.263-.816,1.717-1.407.452-.591.79-1.275,1.012-2.056.22-.78.332-1.602.332-2.466,0-.844-.117-1.646-.349-2.404-.232-.759-.579-1.429-1.043-2.008s-1.038-1.038-1.723-1.376c-.685-.337-1.481-.505-2.388-.505-.971,0-1.802.174-2.498.521-.696.349-1.265.82-1.708,1.416-.443.595-.77,1.288-.981,2.078-.21.792-.316,1.624-.316,2.498Z"/>
|
||||||
|
<path d="m224.423,95.827c-.696,0-1.323.105-1.881.316s-1.034.512-1.423.902c-.391.39-.691.864-.902,1.423s-.316,1.186-.316,1.881v10.026h-2.625v-24.446h2.625v10.531c.253-.516.564-.956.933-1.32s.777-.663,1.226-.902c.447-.237.933-.411,1.454-.521.522-.111,1.057-.166,1.605-.166,1.033,0,1.937.171,2.712.513.775.343,1.423.818,1.945,1.424.522.605.915,1.326,1.178,2.157.263.833.395,1.745.395,2.735v9.994h-2.625v-10.184c0-1.423-.36-2.506-1.083-3.25-.722-.742-1.795-1.114-3.217-1.114Z"/>
|
||||||
|
<path d="m251.685,102.215c0,1.202-.206,2.319-.617,3.352-.411,1.034-.986,1.927-1.723,2.681-.739.753-1.611,1.344-2.618,1.77-1.007.428-2.106.641-3.296.641-1.256,0-2.388-.222-3.4-.665s-1.874-1.053-2.585-1.834-1.257-1.697-1.637-2.752c-.38-1.053-.57-2.192-.57-3.416,0-1.191.201-2.3.601-3.329.4-1.028.964-1.92,1.692-2.68.727-.759,1.594-1.354,2.601-1.787s2.116-.648,3.329-.648c1.254,0,2.391.22,3.408.663s1.881,1.055,2.593,1.835,1.26,1.697,1.644,2.751c.385,1.055.578,2.192.578,3.416Zm-13.726.031c0,.886.117,1.708.349,2.467s.579,1.418,1.043,1.977c.464.558,1.038.995,1.723,1.311.685.317,1.481.476,2.388.476.938,0,1.753-.175,2.443-.522.691-.349,1.263-.816,1.717-1.407.452-.591.79-1.275,1.012-2.056.22-.78.332-1.602.332-2.466,0-.844-.117-1.646-.349-2.404-.232-.759-.579-1.429-1.043-2.008s-1.038-1.038-1.723-1.376c-.685-.337-1.481-.505-2.388-.505-.971,0-1.802.174-2.498.521-.696.349-1.265.82-1.708,1.416-.443.595-.77,1.288-.981,2.078-.21.792-.316,1.624-.316,2.498Z"/>
|
||||||
|
<path d="m281.097,103.923c-.38.959-.884,1.85-1.511,2.672-.627.823-1.346,1.534-2.157,2.135-.812.601-1.703,1.073-2.673,1.415-.969.342-1.976.514-3.02.514-1.76,0-3.312-.295-4.656-.886-1.345-.59-2.472-1.407-3.385-2.45-.912-1.044-1.603-2.279-2.072-3.709-.469-1.428-.704-2.98-.704-4.657,0-1.033.114-2.037.341-3.013.227-.974.553-1.889.98-2.743.428-.854.95-1.637,1.565-2.348.617-.711,1.318-1.32,2.104-1.827.785-.505,1.652-.901,2.601-1.186.949-.284,1.961-.426,3.036-.426,1.012,0,2.003.148,2.973.442.971.295,1.866.718,2.689,1.266.822.548,1.55,1.209,2.182,1.984s1.127,1.642,1.486,2.602l-2.246.98c-.295-.768-.673-1.468-1.13-2.095-.459-.627-.989-1.162-1.59-1.604-.601-.443-1.265-.785-1.992-1.029-.728-.242-1.508-.363-2.341-.363-1.359,0-2.532.268-3.518.806s-1.797,1.254-2.435,2.151c-.638.895-1.107,1.919-1.407,3.067-.301,1.149-.451,2.335-.451,3.558,0,1.244.166,2.424.498,3.543.333,1.117.833,2.098,1.503,2.94.669.844,1.507,1.513,2.514,2.008,1.007.496,2.18.744,3.518.744.801,0,1.576-.143,2.325-.428.749-.284,1.44-.671,2.072-1.162.632-.49,1.191-1.064,1.675-1.723.485-.658.864-1.357,1.139-2.095l2.088.917Z"/>
|
||||||
|
<path d="m287.991,100.539v9.835h-2.751v-22.613h7.431c1.044,0,2.042.111,2.997.332.954.222,1.795.586,2.522,1.092.728.505,1.307,1.168,1.74,1.984.431.818.648,1.822.648,3.013,0,.885-.137,1.673-.411,2.364-.275.691-.66,1.289-1.155,1.795-.496.507-1.086.925-1.771,1.257-.685.333-1.44.583-2.261.752l6.926,10.026h-3.068l-6.736-9.835h-4.112Zm0-2.183h4.871c.727,0,1.393-.075,1.999-.228s1.131-.402,1.574-.744c.442-.342.785-.785,1.028-1.328s.363-1.21.363-2.001c0-.801-.126-1.466-.378-1.992-.254-.527-.601-.943-1.044-1.25-.443-.305-.967-.521-1.573-.648s-1.262-.189-1.968-.189h-4.871v8.38Z"/>
|
||||||
|
<path d="m308.231,91.335v19.039h-2.529v-22.613h3.826l7.242,17.932,7.148-17.932h3.795v22.613h-2.752v-19.039l-7.653,19.039h-1.17l-7.907-19.039Z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 10 KiB |
@@ -3,6 +3,7 @@ import Box from '@mui/material/Box';
|
|||||||
import Toolbar from '@mui/material/Toolbar';
|
import Toolbar from '@mui/material/Toolbar';
|
||||||
import { useTheme } from '@mui/material/styles';
|
import { useTheme } from '@mui/material/styles';
|
||||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||||
|
import Stack from '@mui/material/Stack';
|
||||||
import AppsIcon from '@mui/icons-material/Apps';
|
import AppsIcon from '@mui/icons-material/Apps';
|
||||||
import SwapCallsIcon from '@mui/icons-material/SwapCalls';
|
import SwapCallsIcon from '@mui/icons-material/SwapCalls';
|
||||||
import HistoryIcon from '@mui/icons-material/History';
|
import HistoryIcon from '@mui/icons-material/History';
|
||||||
@@ -139,7 +140,7 @@ export default function PublicLayout({
|
|||||||
onDrawerClose={closeDrawer}
|
onDrawerClose={closeDrawer}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Box sx={{ display: 'flex' }}>
|
<Box sx={{ display: 'flex', height: '100%' }}>
|
||||||
<Drawer
|
<Drawer
|
||||||
links={drawerLinks}
|
links={drawerLinks}
|
||||||
bottomLinks={bottomLinks}
|
bottomLinks={bottomLinks}
|
||||||
@@ -148,11 +149,10 @@ export default function PublicLayout({
|
|||||||
onClose={closeDrawer}
|
onClose={closeDrawer}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Box sx={{ flex: 1 }}>
|
<Stack flex={1}>
|
||||||
<Toolbar />
|
<Toolbar />
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
</Box>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
44
packages/web/src/components/NotFound/index.tsx
Normal file
44
packages/web/src/components/NotFound/index.tsx
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import Stack from '@mui/material/Stack';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
|
||||||
|
import * as URLS from 'config/urls';
|
||||||
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
|
import useAuthentication from 'hooks/useAuthentication';
|
||||||
|
import Layout from 'components/Layout';
|
||||||
|
import PublicLayout from 'components/PublicLayout';
|
||||||
|
|
||||||
|
export default function NoResultFound(): React.ReactElement {
|
||||||
|
const formatMessage = useFormatMessage();
|
||||||
|
const { isAuthenticated } = useAuthentication();
|
||||||
|
|
||||||
|
const pageContent = (
|
||||||
|
<Stack
|
||||||
|
justifyContent="center"
|
||||||
|
alignItems="center"
|
||||||
|
flex={1}
|
||||||
|
spacing={1}
|
||||||
|
p={2}
|
||||||
|
mb={11}
|
||||||
|
>
|
||||||
|
<Typography variant="h1" color="primary" textAlign="center">
|
||||||
|
404
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1" textAlign="center">
|
||||||
|
{formatMessage('notFoundPage.title')}
|
||||||
|
</Typography>
|
||||||
|
<Link to={isAuthenticated ? URLS.FLOWS : URLS.LOGIN}>
|
||||||
|
<Button variant="contained" sx={{ mt: 3 }} component="div">
|
||||||
|
{formatMessage('notFoundPage.button')}
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
|
||||||
|
return isAuthenticated ? (
|
||||||
|
<Layout>{pageContent}</Layout>
|
||||||
|
) : (
|
||||||
|
<PublicLayout>{pageContent}</PublicLayout>
|
||||||
|
);
|
||||||
|
}
|
@@ -265,5 +265,7 @@
|
|||||||
"authClient.buttonSubmit": "Submit",
|
"authClient.buttonSubmit": "Submit",
|
||||||
"authClient.inputName": "Name",
|
"authClient.inputName": "Name",
|
||||||
"authClient.inputActive": "Active",
|
"authClient.inputActive": "Active",
|
||||||
"updateAuthClient.title": "Update auth client"
|
"updateAuthClient.title": "Update auth client",
|
||||||
|
"notFoundPage.title": "We can't seem to find a page you're looking for.",
|
||||||
|
"notFoundPage.button": "Back to home page"
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { Route, Routes, Navigate } from 'react-router-dom';
|
import { Route, Routes as ReactRouterRoutes, Navigate } from 'react-router-dom';
|
||||||
import Layout from 'components/Layout';
|
import Layout from 'components/Layout';
|
||||||
|
import NoResultFound from 'components/NotFound';
|
||||||
import PublicLayout from 'components/PublicLayout';
|
import PublicLayout from 'components/PublicLayout';
|
||||||
import Applications from 'pages/Applications';
|
import Applications from 'pages/Applications';
|
||||||
import Application from 'pages/Application';
|
import Application from 'pages/Application';
|
||||||
@@ -17,127 +18,134 @@ import * as URLS from 'config/urls';
|
|||||||
import settingsRoutes from './settingsRoutes';
|
import settingsRoutes from './settingsRoutes';
|
||||||
import adminSettingsRoutes from './adminSettingsRoutes';
|
import adminSettingsRoutes from './adminSettingsRoutes';
|
||||||
import Notifications from 'pages/Notifications';
|
import Notifications from 'pages/Notifications';
|
||||||
|
import useConfig from 'hooks/useConfig';
|
||||||
|
import useAuthentication from 'hooks/useAuthentication';
|
||||||
|
|
||||||
export default (
|
function Routes() {
|
||||||
<Routes>
|
const { config } = useConfig();
|
||||||
<Route
|
const { isAuthenticated } = useAuthentication();
|
||||||
path={URLS.EXECUTIONS}
|
|
||||||
element={
|
|
||||||
<Layout>
|
|
||||||
<Executions />
|
|
||||||
</Layout>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Route
|
return (
|
||||||
path={URLS.EXECUTION_PATTERN}
|
<ReactRouterRoutes>
|
||||||
element={
|
<Route
|
||||||
<Layout>
|
path={URLS.EXECUTIONS}
|
||||||
<Execution />
|
element={
|
||||||
</Layout>
|
<Layout>
|
||||||
}
|
<Executions />
|
||||||
/>
|
</Layout>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path={URLS.FLOWS}
|
path={URLS.EXECUTION_PATTERN}
|
||||||
element={
|
element={
|
||||||
<Layout>
|
<Layout>
|
||||||
<Flows />
|
<Execution />
|
||||||
</Layout>
|
</Layout>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path={URLS.FLOW_PATTERN}
|
path={URLS.FLOWS}
|
||||||
element={
|
element={
|
||||||
<Layout>
|
<Layout>
|
||||||
<Flow />
|
<Flows />
|
||||||
</Layout>
|
</Layout>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path={`${URLS.APPS}/*`}
|
path={URLS.FLOW_PATTERN}
|
||||||
element={
|
element={
|
||||||
<Layout>
|
<Layout>
|
||||||
<Applications />
|
<Flow />
|
||||||
</Layout>
|
</Layout>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path={`${URLS.APP_PATTERN}/*`}
|
path={`${URLS.APPS}/*`}
|
||||||
element={
|
element={
|
||||||
<Layout>
|
<Layout>
|
||||||
<Application />
|
<Applications />
|
||||||
</Layout>
|
</Layout>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Route path={`${URLS.EDITOR}/*`} element={<EditorRoutes />} />
|
<Route
|
||||||
|
path={`${URLS.APP_PATTERN}/*`}
|
||||||
|
element={
|
||||||
|
<Layout>
|
||||||
|
<Application />
|
||||||
|
</Layout>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<Route
|
<Route path={`${URLS.EDITOR}/*`} element={<EditorRoutes />} />
|
||||||
path={URLS.LOGIN}
|
|
||||||
element={
|
|
||||||
<PublicLayout>
|
|
||||||
<Login />
|
|
||||||
</PublicLayout>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path={URLS.LOGIN_CALLBACK}
|
path={URLS.LOGIN}
|
||||||
element={<LoginCallback />}
|
element={
|
||||||
/>
|
<PublicLayout>
|
||||||
|
<Login />
|
||||||
|
</PublicLayout>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<Route
|
<Route path={URLS.LOGIN_CALLBACK} element={<LoginCallback />} />
|
||||||
path={URLS.SIGNUP}
|
|
||||||
element={
|
|
||||||
<PublicLayout>
|
|
||||||
<SignUp />
|
|
||||||
</PublicLayout>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path={URLS.FORGOT_PASSWORD}
|
path={URLS.SIGNUP}
|
||||||
element={
|
element={
|
||||||
<PublicLayout>
|
<PublicLayout>
|
||||||
<ForgotPassword />
|
<SignUp />
|
||||||
</PublicLayout>
|
</PublicLayout>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path={URLS.RESET_PASSWORD}
|
path={URLS.FORGOT_PASSWORD}
|
||||||
element={
|
element={
|
||||||
<PublicLayout>
|
<PublicLayout>
|
||||||
<ResetPassword />
|
<ForgotPassword />
|
||||||
</PublicLayout>
|
</PublicLayout>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path={URLS.UPDATES}
|
path={URLS.RESET_PASSWORD}
|
||||||
element={
|
element={
|
||||||
<Layout>
|
<PublicLayout>
|
||||||
<Notifications />
|
<ResetPassword />
|
||||||
</Layout>
|
</PublicLayout>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Route path="/" element={<Navigate to={URLS.FLOWS} replace />} />
|
{!config?.disableNotificationsPage && (
|
||||||
|
<Route
|
||||||
|
path={URLS.UPDATES}
|
||||||
|
element={
|
||||||
|
<Layout>
|
||||||
|
<Notifications />
|
||||||
|
</Layout>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<Route path={URLS.SETTINGS}>{settingsRoutes}</Route>
|
<Route
|
||||||
|
path="/"
|
||||||
|
element={
|
||||||
|
<Navigate to={isAuthenticated ? URLS.FLOWS : URLS.LOGIN} replace />
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<Route path={URLS.ADMIN_SETTINGS}>{adminSettingsRoutes}</Route>
|
<Route path={URLS.SETTINGS}>{settingsRoutes}</Route>
|
||||||
|
|
||||||
<Route
|
<Route path={URLS.ADMIN_SETTINGS}>{adminSettingsRoutes}</Route>
|
||||||
element={
|
|
||||||
<Layout>
|
<Route path="*" element={<NoResultFound />} />
|
||||||
<div>404</div>
|
</ReactRouterRoutes>
|
||||||
</Layout>
|
);
|
||||||
}
|
}
|
||||||
/>
|
|
||||||
</Routes>
|
export default <Routes />;
|
||||||
);
|
|
||||||
|
79
yarn.lock
79
yarn.lock
@@ -3952,11 +3952,6 @@
|
|||||||
resolved "https://registry.npmjs.org/@types/node/-/node-17.0.10.tgz"
|
resolved "https://registry.npmjs.org/@types/node/-/node-17.0.10.tgz"
|
||||||
integrity sha512-S/3xB4KzyFxYGCppyDt68yzBU9ysL88lSdIah4D6cptdcltc4NCPCAMc0+PCpg/lLIyC7IPvj2Z52OJWeIUkog==
|
integrity sha512-S/3xB4KzyFxYGCppyDt68yzBU9ysL88lSdIah4D6cptdcltc4NCPCAMc0+PCpg/lLIyC7IPvj2Z52OJWeIUkog==
|
||||||
|
|
||||||
"@types/node@>=8.1.0":
|
|
||||||
version "18.14.5"
|
|
||||||
resolved "https://registry.npmjs.org/@types/node/-/node-18.14.5.tgz"
|
|
||||||
integrity sha512-CRT4tMK/DHYhw1fcCEBwME9CSaZNclxfzVMe7GsO6ULSwsttbj70wSiX6rZdIjGblu93sTJxLdhNIT85KKI7Qw==
|
|
||||||
|
|
||||||
"@types/node@^12.0.0":
|
"@types/node@^12.0.0":
|
||||||
version "12.20.42"
|
version "12.20.42"
|
||||||
resolved "https://registry.npmjs.org/@types/node/-/node-12.20.42.tgz"
|
resolved "https://registry.npmjs.org/@types/node/-/node-12.20.42.tgz"
|
||||||
@@ -6372,19 +6367,6 @@ cookiejar@^2.1.4:
|
|||||||
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.4.tgz#ee669c1fea2cf42dc31585469d193fef0d65771b"
|
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.4.tgz#ee669c1fea2cf42dc31585469d193fef0d65771b"
|
||||||
integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==
|
integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==
|
||||||
|
|
||||||
copyfiles@^2.4.1:
|
|
||||||
version "2.4.1"
|
|
||||||
resolved "https://registry.npmjs.org/copyfiles/-/copyfiles-2.4.1.tgz"
|
|
||||||
integrity sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==
|
|
||||||
dependencies:
|
|
||||||
glob "^7.0.5"
|
|
||||||
minimatch "^3.0.3"
|
|
||||||
mkdirp "^1.0.4"
|
|
||||||
noms "0.0.0"
|
|
||||||
through2 "^2.0.1"
|
|
||||||
untildify "^4.0.0"
|
|
||||||
yargs "^16.1.0"
|
|
||||||
|
|
||||||
core-js-compat@^3.20.0, core-js-compat@^3.20.2:
|
core-js-compat@^3.20.0, core-js-compat@^3.20.2:
|
||||||
version "3.20.3"
|
version "3.20.3"
|
||||||
resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.20.3.tgz"
|
resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.20.3.tgz"
|
||||||
@@ -8655,7 +8637,7 @@ glob-to-regexp@^0.4.1:
|
|||||||
resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz"
|
resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz"
|
||||||
integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
|
integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
|
||||||
|
|
||||||
glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
|
glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
|
||||||
version "7.2.0"
|
version "7.2.0"
|
||||||
resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz"
|
resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz"
|
||||||
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
|
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
|
||||||
@@ -8791,11 +8773,6 @@ graphql-tools@^8.2.0:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
"@apollo/client" "~3.2.5 || ~3.3.0 || ~3.4.0"
|
"@apollo/client" "~3.2.5 || ~3.3.0 || ~3.4.0"
|
||||||
|
|
||||||
graphql-type-json@^0.3.2:
|
|
||||||
version "0.3.2"
|
|
||||||
resolved "https://registry.npmjs.org/graphql-type-json/-/graphql-type-json-0.3.2.tgz"
|
|
||||||
integrity sha512-J+vjof74oMlCWXSvt0DOf2APEdZOCdubEvGDUAlqH//VBYcOYsGgRW7Xzorr44LvkjiuvecWc8fChxuZZbChtg==
|
|
||||||
|
|
||||||
graphql@^15.6.0:
|
graphql@^15.6.0:
|
||||||
version "15.8.0"
|
version "15.8.0"
|
||||||
resolved "https://registry.npmjs.org/graphql/-/graphql-15.8.0.tgz"
|
resolved "https://registry.npmjs.org/graphql/-/graphql-15.8.0.tgz"
|
||||||
@@ -9249,7 +9226,7 @@ inflight@^1.0.4:
|
|||||||
once "^1.3.0"
|
once "^1.3.0"
|
||||||
wrappy "1"
|
wrappy "1"
|
||||||
|
|
||||||
inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3:
|
inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3:
|
||||||
version "2.0.4"
|
version "2.0.4"
|
||||||
resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
|
resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
|
||||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||||
@@ -9671,11 +9648,6 @@ is-yarn-global@^0.3.0:
|
|||||||
resolved "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz"
|
resolved "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz"
|
||||||
integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==
|
integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==
|
||||||
|
|
||||||
isarray@0.0.1:
|
|
||||||
version "0.0.1"
|
|
||||||
resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
|
|
||||||
integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
|
|
||||||
|
|
||||||
isarray@~1.0.0:
|
isarray@~1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz"
|
||||||
@@ -11060,13 +11032,6 @@ minimatch@3.0.4, minimatch@^3.0.4:
|
|||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion "^1.1.7"
|
brace-expansion "^1.1.7"
|
||||||
|
|
||||||
minimatch@^3.0.3:
|
|
||||||
version "3.1.2"
|
|
||||||
resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz"
|
|
||||||
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
|
|
||||||
dependencies:
|
|
||||||
brace-expansion "^1.1.7"
|
|
||||||
|
|
||||||
minimatch@^5.0.1:
|
minimatch@^5.0.1:
|
||||||
version "5.1.0"
|
version "5.1.0"
|
||||||
resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz"
|
resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz"
|
||||||
@@ -11514,14 +11479,6 @@ nodemon@^2.0.13:
|
|||||||
undefsafe "^2.0.5"
|
undefsafe "^2.0.5"
|
||||||
update-notifier "^5.1.0"
|
update-notifier "^5.1.0"
|
||||||
|
|
||||||
noms@0.0.0:
|
|
||||||
version "0.0.0"
|
|
||||||
resolved "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz"
|
|
||||||
integrity sha1-2o69nzr51nYJGbJ9nNyAkqczKFk=
|
|
||||||
dependencies:
|
|
||||||
inherits "^2.0.1"
|
|
||||||
readable-stream "~1.0.31"
|
|
||||||
|
|
||||||
nopt@^4.0.1:
|
nopt@^4.0.1:
|
||||||
version "4.0.3"
|
version "4.0.3"
|
||||||
resolved "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz"
|
resolved "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz"
|
||||||
@@ -13635,16 +13592,6 @@ readable-stream@^2.0.1, readable-stream@^2.0.6, readable-stream@^2.2.2, readable
|
|||||||
string_decoder "~1.1.1"
|
string_decoder "~1.1.1"
|
||||||
util-deprecate "~1.0.1"
|
util-deprecate "~1.0.1"
|
||||||
|
|
||||||
readable-stream@~1.0.31:
|
|
||||||
version "1.0.34"
|
|
||||||
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz"
|
|
||||||
integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=
|
|
||||||
dependencies:
|
|
||||||
core-util-is "~1.0.0"
|
|
||||||
inherits "~2.0.1"
|
|
||||||
isarray "0.0.1"
|
|
||||||
string_decoder "~0.10.x"
|
|
||||||
|
|
||||||
readdir-scoped-modules@^1.0.0:
|
readdir-scoped-modules@^1.0.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz"
|
resolved "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz"
|
||||||
@@ -14771,11 +14718,6 @@ string_decoder@^1.1.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
safe-buffer "~5.2.0"
|
safe-buffer "~5.2.0"
|
||||||
|
|
||||||
string_decoder@~0.10.x:
|
|
||||||
version "0.10.31"
|
|
||||||
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
|
|
||||||
integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=
|
|
||||||
|
|
||||||
string_decoder@~1.1.1:
|
string_decoder@~1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz"
|
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz"
|
||||||
@@ -14862,14 +14804,6 @@ strip-literal@^1.3.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
acorn "^8.10.0"
|
acorn "^8.10.0"
|
||||||
|
|
||||||
stripe@^11.13.0:
|
|
||||||
version "11.13.0"
|
|
||||||
resolved "https://registry.npmjs.org/stripe/-/stripe-11.13.0.tgz"
|
|
||||||
integrity sha512-Jx0nDbdvRsTtDSX5OFQ+4rLmYIftoiOE9HAXWIgyhAz1QjRFI3UIiJ/kCyhkdJBoHu019O5Ya6EmQ5Zf635XDw==
|
|
||||||
dependencies:
|
|
||||||
"@types/node" ">=8.1.0"
|
|
||||||
qs "^6.11.0"
|
|
||||||
|
|
||||||
strnum@^1.0.5:
|
strnum@^1.0.5:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz"
|
resolved "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz"
|
||||||
@@ -15167,7 +15101,7 @@ throat@^6.0.1:
|
|||||||
resolved "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz"
|
resolved "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz"
|
||||||
integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==
|
integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==
|
||||||
|
|
||||||
through2@^2.0.0, through2@^2.0.1:
|
through2@^2.0.0:
|
||||||
version "2.0.5"
|
version "2.0.5"
|
||||||
resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz"
|
resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz"
|
||||||
integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==
|
integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==
|
||||||
@@ -15572,11 +15506,6 @@ unquote@~1.1.1:
|
|||||||
resolved "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz"
|
resolved "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz"
|
||||||
integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=
|
integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=
|
||||||
|
|
||||||
untildify@^4.0.0:
|
|
||||||
version "4.0.0"
|
|
||||||
resolved "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz"
|
|
||||||
integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==
|
|
||||||
|
|
||||||
upath@^1.2.0:
|
upath@^1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz"
|
resolved "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz"
|
||||||
@@ -16496,7 +16425,7 @@ yargs-parser@^20.2.2, yargs-parser@^20.2.3:
|
|||||||
resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz"
|
resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz"
|
||||||
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
|
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
|
||||||
|
|
||||||
yargs@^16.1.0, yargs@^16.2.0:
|
yargs@^16.2.0:
|
||||||
version "16.2.0"
|
version "16.2.0"
|
||||||
resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz"
|
resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz"
|
||||||
integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
|
integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
|
||||||
|
Reference in New Issue
Block a user