Merge branch 'main' into show-webhook-url-by-flag
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
"@sentry/node": "^7.42.0",
|
||||
"@sentry/tracing": "^7.42.0",
|
||||
"@types/luxon": "^2.3.1",
|
||||
"@types/xmlrpc": "^1.3.7",
|
||||
"ajv-formats": "^2.1.1",
|
||||
"axios": "0.24.0",
|
||||
"bcrypt": "^5.0.1",
|
||||
@@ -62,7 +63,8 @@
|
||||
"pg": "^8.7.1",
|
||||
"php-serialize": "^4.0.2",
|
||||
"stripe": "^11.13.0",
|
||||
"winston": "^3.7.1"
|
||||
"winston": "^3.7.1",
|
||||
"xmlrpc": "^1.3.2"
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
|
@@ -19,8 +19,8 @@ export default {
|
||||
|
||||
channels.data = response.data
|
||||
.filter((channel: IJSONObject) => {
|
||||
// filter in text channels only
|
||||
return channel.type === 0;
|
||||
// filter in text channels and announcement channels only
|
||||
return channel.type === 0 || channel.type === 5;
|
||||
})
|
||||
.map((channel: IJSONObject) => {
|
||||
return {
|
||||
|
@@ -0,0 +1,105 @@
|
||||
import defineAction from '../../../../helpers/define-action';
|
||||
|
||||
type THeaders = {
|
||||
__id: string;
|
||||
header: string;
|
||||
}[];
|
||||
|
||||
export default defineAction({
|
||||
name: 'Create spreadsheet',
|
||||
key: 'createSpreadsheet',
|
||||
description:
|
||||
'Create a blank spreadsheet or duplicate an existing spreadsheet. Optionally, provide headers.',
|
||||
arguments: [
|
||||
{
|
||||
label: 'Title',
|
||||
key: 'title',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: '',
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'Spreadsheet to copy',
|
||||
key: 'spreadsheetId',
|
||||
type: 'dropdown' as const,
|
||||
required: false,
|
||||
description: 'Choose a spreadsheet to copy its data.',
|
||||
variables: true,
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listSpreadsheets',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Headers',
|
||||
key: 'headers',
|
||||
type: 'dynamic' as const,
|
||||
required: false,
|
||||
description:
|
||||
'These headers are ignored if "Spreadsheet to Copy" is selected.',
|
||||
fields: [
|
||||
{
|
||||
label: 'Header',
|
||||
key: 'header',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
variables: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
if ($.step.parameters.spreadsheetId) {
|
||||
const body = { name: $.step.parameters.title };
|
||||
|
||||
const { data } = await $.http.post(
|
||||
`https://www.googleapis.com/drive/v3/files/${$.step.parameters.spreadsheetId}/copy`,
|
||||
body
|
||||
);
|
||||
|
||||
$.setActionItem({
|
||||
raw: data,
|
||||
});
|
||||
} else {
|
||||
const headers = $.step.parameters.headers as THeaders;
|
||||
const values = headers.map((entry) => entry.header);
|
||||
|
||||
const spreadsheetBody = {
|
||||
properties: {
|
||||
title: $.step.parameters.title,
|
||||
},
|
||||
sheets: [
|
||||
{
|
||||
data: [
|
||||
{
|
||||
startRow: 0,
|
||||
startColumn: 0,
|
||||
rowData: [
|
||||
{
|
||||
values: values.map((header) => ({
|
||||
userEnteredValue: { stringValue: header },
|
||||
})),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const { data } = await $.http.post('/v4/spreadsheets', spreadsheetBody);
|
||||
|
||||
$.setActionItem({
|
||||
raw: data,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
@@ -1,3 +1,4 @@
|
||||
import createSpreadsheet from './create-spreadsheet';
|
||||
import createSpreadsheetRow from './create-spreadsheet-row';
|
||||
|
||||
export default [createSpreadsheetRow];
|
||||
export default [createSpreadsheet, createSpreadsheetRow];
|
||||
|
3
packages/backend/src/apps/mattermost/actions/index.ts
Normal file
3
packages/backend/src/apps/mattermost/actions/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import sendMessageToChannel from './send-a-message-to-channel';
|
||||
|
||||
export default [sendMessageToChannel];
|
@@ -0,0 +1,42 @@
|
||||
import defineAction from '../../../../helpers/define-action';
|
||||
import postMessage from './post-message';
|
||||
|
||||
export default defineAction({
|
||||
name: 'Send a message to channel',
|
||||
key: 'sendMessageToChannel',
|
||||
description: 'Sends a message to a channel you specify.',
|
||||
arguments: [
|
||||
{
|
||||
label: 'Channel',
|
||||
key: 'channel',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
description: 'Pick a channel to send the message to.',
|
||||
variables: true,
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listChannels',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Message text',
|
||||
key: 'message',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'The content of your new message.',
|
||||
variables: true,
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
const message = await postMessage($);
|
||||
|
||||
return message;
|
||||
},
|
||||
});
|
@@ -0,0 +1,27 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
|
||||
type TData = {
|
||||
channel_id: string;
|
||||
message: string;
|
||||
};
|
||||
|
||||
const postMessage = async ($: IGlobalVariable) => {
|
||||
const { parameters } = $.step;
|
||||
const channel_id = parameters.channel as string;
|
||||
const message = parameters.message as string;
|
||||
|
||||
const data: TData = {
|
||||
channel_id,
|
||||
message,
|
||||
};
|
||||
|
||||
const response = await $.http.post('/api/v4/posts', data);
|
||||
|
||||
const actionData = {
|
||||
raw: response?.data,
|
||||
};
|
||||
|
||||
$.setActionItem(actionData);
|
||||
};
|
||||
|
||||
export default postMessage;
|
6
packages/backend/src/apps/mattermost/assets/favicon.svg
Normal file
6
packages/backend/src/apps/mattermost/assets/favicon.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="256px" height="256px" viewBox="0 0 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
|
||||
<g>
|
||||
<path d="M6.79123171,86.9648684 C25.2351716,32.4823178 76.783459,-1.43234143 131.421839,0.0464773399 L131.421839,0.0464773399 L113.909757,20.739032 C81.4957329,26.5997669 53.5072568,48.7337413 42.5072761,81.2287969 C26.140539,129.576353 53.572705,182.545803 103.779303,199.543648 C153.985902,216.538377 207.952658,191.12264 224.319395,142.7782 C235.283535,110.390667 226.589826,75.9306053 204.563374,51.5978814 L204.563374,51.5978814 L203.21701,24.4290666 C247.371203,56.4768925 267.622761,114.633895 249.208429,169.029181 C226.546194,235.970273 153.909545,271.865521 86.9684532,249.204844 C20.0273609,226.542609 -15.8694453,153.905961 6.79123171,86.9648684 Z M165.185344,11.9237762 C165.839826,11.6401671 166.594039,11.5793938 167.321762,11.8256038 C168.035459,12.0671391 168.585536,12.5580009 168.936152,13.1595015 L168.936152,13.1595015 L169.007833,13.2763734 L169.071723,13.4103864 C169.240019,13.7313945 169.383381,14.0991514 169.450388,14.5510559 C169.582343,15.4417519 169.641535,17.5358595 169.665634,19.6808502 L169.671365,20.2662434 C169.677102,20.9486534 169.679633,21.6256073 169.680171,22.2599793 L169.680173,22.7924325 C169.678741,24.5267431 169.663874,25.8268542 169.663874,25.8268542 L169.663874,25.8268542 L170.167202,44.7600977 L170.910507,66.6151379 L171.837691,104.59538 C171.837691,104.59538 171.83785,104.602367 171.838064,104.616156 L171.838772,104.677745 C171.838883,104.691349 171.838983,104.706608 171.839058,104.723498 L171.839105,104.844231 C171.832023,107.013302 171.387173,122.892918 160.122454,133.928662 C148.009853,145.795053 133.131285,144.708923 123.451177,141.433394 C113.771069,138.154749 101.293828,129.979951 98.8800345,113.195592 C96.8283098,98.9302108 104.41287,86.9390787 106.734401,83.6627102 L106.889339,83.4459953 C107.205256,83.0081712 107.389865,82.7777388 107.389865,82.7777388 L107.389865,82.7777388 L131.197445,53.1717559 L145.064682,36.2627333 L156.965355,21.5275276 C156.965355,21.5275276 158.715313,19.1834331 160.51647,16.874806 L160.876881,16.4142586 C161.477025,15.6498178 162.070275,14.9069442 162.593713,14.2737698 L162.898895,13.907734 C163.342593,13.3805415 163.71955,12.9564826 163.983901,12.6998055 C164.292443,12.4006135 164.608776,12.205827 164.918876,12.0546727 L164.918876,12.0546727 L165.146386,11.9393591 Z" fill="#0058CC"></path>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,18 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
import { URL, URLSearchParams } from 'url';
|
||||
import getBaseUrl from '../common/get-base-url';
|
||||
|
||||
export default async function generateAuthUrl($: IGlobalVariable) {
|
||||
const searchParams = new URLSearchParams({
|
||||
client_id: $.auth.data.clientId as string,
|
||||
redirect_uri: $.auth.data.oAuthRedirectUrl as string,
|
||||
response_type: 'code',
|
||||
});
|
||||
|
||||
const baseUrl = getBaseUrl($);
|
||||
const path = `/oauth/authorize?${searchParams.toString()}`;
|
||||
|
||||
await $.auth.set({
|
||||
url: new URL(path, baseUrl).toString(),
|
||||
});
|
||||
}
|
57
packages/backend/src/apps/mattermost/auth/index.ts
Normal file
57
packages/backend/src/apps/mattermost/auth/index.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import generateAuthUrl from './generate-auth-url';
|
||||
import verifyCredentials from './verify-credentials';
|
||||
import isStillVerified from './is-still-verified';
|
||||
|
||||
export default {
|
||||
fields: [
|
||||
{
|
||||
key: 'oAuthRedirectUrl',
|
||||
label: 'OAuth Redirect URL',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: true,
|
||||
value: '{WEB_APP_URL}/app/mattermost/connections/add',
|
||||
placeholder: null,
|
||||
description:
|
||||
'When asked to input an OAuth callback or redirect URL in Mattermost OAuth, enter the URL above.',
|
||||
clickToCopy: true,
|
||||
},
|
||||
{
|
||||
key: 'instanceUrl',
|
||||
label: 'Mattermost instance URL',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
description: 'Your Mattermost instance URL',
|
||||
clickToCopy: true,
|
||||
},
|
||||
{
|
||||
key: 'clientId',
|
||||
label: 'Client id',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
description: null,
|
||||
clickToCopy: false,
|
||||
},
|
||||
{
|
||||
key: 'clientSecret',
|
||||
label: 'Client secret',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
description: null,
|
||||
clickToCopy: false,
|
||||
},
|
||||
],
|
||||
|
||||
generateAuthUrl,
|
||||
verifyCredentials,
|
||||
isStillVerified,
|
||||
};
|
@@ -0,0 +1,9 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
import getCurrentUser from '../common/get-current-user';
|
||||
|
||||
const isStillVerified = async ($: IGlobalVariable) => {
|
||||
const user = await getCurrentUser($);
|
||||
return !!user.id;
|
||||
};
|
||||
|
||||
export default isStillVerified;
|
@@ -0,0 +1,44 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
import getCurrentUser from '../common/get-current-user';
|
||||
|
||||
const verifyCredentials = async ($: IGlobalVariable) => {
|
||||
const oauthRedirectUrlField = $.app.auth.fields.find(
|
||||
(field) => field.key == 'oAuthRedirectUrl'
|
||||
);
|
||||
const redirectUri = oauthRedirectUrlField.value as string;
|
||||
const params = {
|
||||
client_id: $.auth.data.clientId,
|
||||
client_secret: $.auth.data.clientSecret,
|
||||
code: $.auth.data.code,
|
||||
grant_type: 'authorization_code',
|
||||
redirect_uri: redirectUri,
|
||||
};
|
||||
const headers = {
|
||||
'Content-Type': 'application/x-www-form-urlencoded', // This is not documented yet required
|
||||
};
|
||||
const response = await $.http.post('/oauth/access_token', null, {
|
||||
params,
|
||||
headers,
|
||||
});
|
||||
|
||||
const {
|
||||
data: { access_token, refresh_token, scope, token_type },
|
||||
} = response;
|
||||
|
||||
$.auth.data.accessToken = response.data.access_token;
|
||||
|
||||
const currentUser = await getCurrentUser($);
|
||||
|
||||
await $.auth.set({
|
||||
clientId: $.auth.data.clientId,
|
||||
clientSecret: $.auth.data.clientSecret,
|
||||
accessToken: access_token,
|
||||
refreshToken: refresh_token,
|
||||
scope: scope,
|
||||
tokenType: token_type,
|
||||
userId: currentUser.id,
|
||||
screenName: currentUser.username,
|
||||
});
|
||||
};
|
||||
|
||||
export default verifyCredentials;
|
@@ -0,0 +1,12 @@
|
||||
import { TBeforeRequest } from '@automatisch/types';
|
||||
|
||||
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
|
||||
if ($.auth.data?.accessToken) {
|
||||
requestConfig.headers = requestConfig.headers || {};
|
||||
requestConfig.headers.Authorization = `Bearer ${$.auth.data.accessToken}`;
|
||||
}
|
||||
|
||||
return requestConfig;
|
||||
};
|
||||
|
||||
export default addAuthHeader;
|
@@ -0,0 +1,11 @@
|
||||
import { TBeforeRequest } from '@automatisch/types';
|
||||
|
||||
const addXRequestedWithHeader: TBeforeRequest = ($, requestConfig) => {
|
||||
// This is not documented yet required
|
||||
// ref. https://forum.mattermost.com/t/solved-invalid-or-expired-session-please-login-again/6772
|
||||
requestConfig.headers = requestConfig.headers || {};
|
||||
requestConfig.headers['X-Requested-With'] = `XMLHttpRequest`;
|
||||
return requestConfig;
|
||||
};
|
||||
|
||||
export default addXRequestedWithHeader;
|
@@ -0,0 +1,7 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
|
||||
const getBaseUrl = ($: IGlobalVariable): string => {
|
||||
return $.auth.data.instanceUrl as string;
|
||||
};
|
||||
|
||||
export default getBaseUrl;
|
@@ -0,0 +1,9 @@
|
||||
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
||||
|
||||
const getCurrentUser = async ($: IGlobalVariable): Promise<IJSONObject> => {
|
||||
const response = await $.http.get('/api/v4/users/me');
|
||||
const currentUser = response.data;
|
||||
return currentUser;
|
||||
};
|
||||
|
||||
export default getCurrentUser;
|
@@ -0,0 +1,9 @@
|
||||
import { TBeforeRequest } from '@automatisch/types';
|
||||
|
||||
const setBaseUrl: TBeforeRequest = ($, requestConfig) => {
|
||||
requestConfig.baseURL = $.auth.data.instanceUrl as string;
|
||||
|
||||
return requestConfig;
|
||||
};
|
||||
|
||||
export default setBaseUrl;
|
@@ -0,0 +1,3 @@
|
||||
import listChannels from './list-channels';
|
||||
|
||||
export default [listChannels];
|
@@ -0,0 +1,36 @@
|
||||
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
||||
|
||||
type TChannel = {
|
||||
id: string;
|
||||
display_name: string;
|
||||
};
|
||||
|
||||
type TResponse = {
|
||||
data: TChannel[];
|
||||
};
|
||||
|
||||
export default {
|
||||
name: 'List channels',
|
||||
key: 'listChannels',
|
||||
|
||||
async run($: IGlobalVariable) {
|
||||
const channels: {
|
||||
data: IJSONObject[];
|
||||
error: IJSONObject | null;
|
||||
} = {
|
||||
data: [],
|
||||
error: null,
|
||||
};
|
||||
|
||||
const response: TResponse = await $.http.get('/api/v4/users/me/channels'); // this endpoint will return only channels user joined, there is no endpoint to list all channels available for user
|
||||
|
||||
for (const channel of response.data) {
|
||||
channels.data.push({
|
||||
value: channel.id as string,
|
||||
name: (channel.display_name as string) || (channel.id as string), // it's possible for channel to not have any name thus falling back to using id
|
||||
});
|
||||
}
|
||||
|
||||
return channels;
|
||||
},
|
||||
};
|
0
packages/backend/src/apps/mattermost/index.d.ts
vendored
Normal file
0
packages/backend/src/apps/mattermost/index.d.ts
vendored
Normal file
22
packages/backend/src/apps/mattermost/index.ts
Normal file
22
packages/backend/src/apps/mattermost/index.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import defineApp from '../../helpers/define-app';
|
||||
import addAuthHeader from './common/add-auth-header';
|
||||
import addXRequestedWithHeader from './common/add-x-requested-with-header';
|
||||
import setBaseUrl from './common/set-base-url';
|
||||
import auth from './auth';
|
||||
import actions from './actions';
|
||||
import dynamicData from './dynamic-data';
|
||||
|
||||
export default defineApp({
|
||||
name: 'Mattermost',
|
||||
key: 'mattermost',
|
||||
iconUrl: '{BASE_URL}/apps/mattermost/assets/favicon.svg',
|
||||
authDocUrl: 'https://automatisch.io/docs/apps/mattermost/connection',
|
||||
baseUrl: 'https://mattermost.com',
|
||||
apiBaseUrl: '', // there is no cloud version of this app, user always need to provide address of own instance when creating connection
|
||||
primaryColor: '4a154b',
|
||||
supportsConnections: true,
|
||||
beforeRequest: [setBaseUrl, addXRequestedWithHeader, addAuthHeader],
|
||||
auth,
|
||||
actions,
|
||||
dynamicData,
|
||||
});
|
103
packages/backend/src/apps/odoo/actions/create-lead/index.ts
Normal file
103
packages/backend/src/apps/odoo/actions/create-lead/index.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import defineAction from '../../../../helpers/define-action';
|
||||
import { authenticate, asyncMethodCall } from '../../common/xmlrpc-client';
|
||||
|
||||
export default defineAction({
|
||||
name: 'Create Lead',
|
||||
key: 'createLead',
|
||||
description: '',
|
||||
arguments: [
|
||||
{
|
||||
label: 'Name',
|
||||
key: 'name',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'Lead name',
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'Type',
|
||||
key: 'type',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
variables: true,
|
||||
options: [
|
||||
{
|
||||
label: 'Lead',
|
||||
value: 'lead'
|
||||
},
|
||||
{
|
||||
label: 'Opportunity',
|
||||
value: 'opportunity'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Email",
|
||||
key: 'email',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
description: 'Email of lead contact',
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: "Contact Name",
|
||||
key: 'contactName',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
description: 'Name of lead contact',
|
||||
variables: true
|
||||
},
|
||||
{
|
||||
label: 'Phone Number',
|
||||
key: 'phoneNumber',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
description: 'Phone number of lead contact',
|
||||
variables: true
|
||||
},
|
||||
{
|
||||
label: 'Mobile Number',
|
||||
key: 'mobileNumber',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
description: 'Mobile number of lead contact',
|
||||
variables: true
|
||||
}
|
||||
],
|
||||
|
||||
async run($) {
|
||||
const uid = await authenticate($);
|
||||
const id = await asyncMethodCall(
|
||||
$,
|
||||
{
|
||||
method: 'execute_kw',
|
||||
params: [
|
||||
$.auth.data.databaseName,
|
||||
uid,
|
||||
$.auth.data.apiKey,
|
||||
'crm.lead',
|
||||
'create',
|
||||
[
|
||||
{
|
||||
name: $.step.parameters.name,
|
||||
type: $.step.parameters.type,
|
||||
email_from: $.step.parameters.email,
|
||||
contact_name: $.step.parameters.contactName,
|
||||
phone: $.step.parameters.phoneNumber,
|
||||
mobile: $.step.parameters.mobileNumber
|
||||
}
|
||||
]
|
||||
],
|
||||
path: 'object',
|
||||
},
|
||||
);
|
||||
|
||||
$.setActionItem(
|
||||
{
|
||||
raw: {
|
||||
id: id
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
});
|
3
packages/backend/src/apps/odoo/actions/index.ts
Normal file
3
packages/backend/src/apps/odoo/actions/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import createLead from './create-lead';
|
||||
|
||||
export default [createLead];
|
1
packages/backend/src/apps/odoo/assets/favicon.svg
Normal file
1
packages/backend/src/apps/odoo/assets/favicon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="191"><circle cx="527.5" cy="118.4" r="72.4" fill="#888"/><path d="M527.5 161.1c23.6 0 42.7-19.1 42.7-42.7s-19.1-42.7-42.7-42.7-42.7 19.1-42.7 42.7 19.1 42.7 42.7 42.7z" fill="#fff"/><circle cx="374" cy="118.4" r="72.4" fill="#888"/><path d="M374 161.1c23.6 0 42.7-19.1 42.7-42.7S397.6 75.7 374 75.7s-42.7 19.1-42.7 42.7 19.1 42.7 42.7 42.7z" fill="#fff"/><path d="M294.9 117.8v.6c0 40-32.4 72.4-72.4 72.4s-72.4-32.4-72.4-72.4S182.5 46 222.5 46c16.4 0 31.5 5.5 43.7 14.6V14.4A14.34 14.34 0 0 1 280.6 0c7.9 0 14.4 6.5 14.4 14.4v102.7c0 .2 0 .5-.1.7z" fill="#888"/><circle cx="222.5" cy="118.4" r="42.7" fill="#fff"/><circle cx="72.4" cy="118.2" r="72.4" fill="#9c5789"/><circle cx="71.7" cy="118.5" r="42.7" fill="#fff"/><script xmlns=""/></svg>
|
After Width: | Height: | Size: 803 B |
65
packages/backend/src/apps/odoo/auth/index.ts
Normal file
65
packages/backend/src/apps/odoo/auth/index.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import verifyCredentials from './verify-credentials';
|
||||
import isStillVerified from './is-still-verified';
|
||||
|
||||
export default {
|
||||
fields: [
|
||||
{
|
||||
key: 'host',
|
||||
label: 'Host Name',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
description: 'Host name of your Odoo Server',
|
||||
clickToCopy: false,
|
||||
},
|
||||
{
|
||||
key: 'port',
|
||||
label: 'Port',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: '443',
|
||||
placeholder: null,
|
||||
description: 'Port that the host is running on, defaults to 443 (HTTPS)',
|
||||
clickToCopy: false,
|
||||
},
|
||||
{
|
||||
key: 'databaseName',
|
||||
label: 'Database Name',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
description: 'Name of your Odoo database',
|
||||
clickToCopy: false,
|
||||
},
|
||||
{
|
||||
key: 'email',
|
||||
label: 'Email Address',
|
||||
type: 'string' as const,
|
||||
requires: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
description: 'Email Address of the account that will be interacting with the database',
|
||||
clickToCopy: false
|
||||
},
|
||||
{
|
||||
key: 'apiKey',
|
||||
label: 'API Key',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
description: 'API Key for your Odoo account',
|
||||
clickToCopy: false
|
||||
}
|
||||
],
|
||||
|
||||
verifyCredentials,
|
||||
isStillVerified
|
||||
};
|
9
packages/backend/src/apps/odoo/auth/is-still-verified.ts
Normal file
9
packages/backend/src/apps/odoo/auth/is-still-verified.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import {IGlobalVariable} from '@automatisch/types';
|
||||
import verifyCredentials from './verify-credentials';
|
||||
|
||||
const isStillVerified = async ($: IGlobalVariable) => {
|
||||
await verifyCredentials($);
|
||||
return true;
|
||||
}
|
||||
|
||||
export default isStillVerified;
|
16
packages/backend/src/apps/odoo/auth/verify-credentials.ts
Normal file
16
packages/backend/src/apps/odoo/auth/verify-credentials.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
import { authenticate } from '../common/xmlrpc-client';
|
||||
|
||||
const verifyCredentials = async ($: IGlobalVariable) => {
|
||||
try {
|
||||
await authenticate($);
|
||||
|
||||
await $.auth.set({
|
||||
screenName: `${$.auth.data.email} @ ${$.auth.data.databaseName} - ${$.auth.data.host}`,
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error('Failed while authorizing!');
|
||||
}
|
||||
}
|
||||
|
||||
export default verifyCredentials;
|
67
packages/backend/src/apps/odoo/common/xmlrpc-client.ts
Normal file
67
packages/backend/src/apps/odoo/common/xmlrpc-client.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { join } from 'node:path';
|
||||
import xmlrpc from 'xmlrpc';
|
||||
import { IGlobalVariable } from "@automatisch/types";
|
||||
|
||||
type AsyncMethodCallPayload = {
|
||||
method: string;
|
||||
params: any[];
|
||||
path?: string;
|
||||
}
|
||||
|
||||
export const asyncMethodCall = async <T = number>($: IGlobalVariable, { method, params, path }: AsyncMethodCallPayload): Promise<T> => {
|
||||
return new Promise(
|
||||
(resolve, reject) => {
|
||||
const client = getClient($, { path });
|
||||
|
||||
client.methodCall(
|
||||
method,
|
||||
params,
|
||||
(error, response) => {
|
||||
if (error != null) {
|
||||
// something went wrong on the server side, display the error returned by Odoo
|
||||
reject(error);
|
||||
}
|
||||
|
||||
resolve(response);
|
||||
}
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export const getClient = ($: IGlobalVariable, { path = 'common' }) => {
|
||||
const host = $.auth.data.host as string;
|
||||
const port = Number($.auth.data.port as string);
|
||||
|
||||
return xmlrpc.createClient(
|
||||
{
|
||||
host,
|
||||
port,
|
||||
path: join('/xmlrpc/2', path),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export const authenticate = async ($: IGlobalVariable) => {
|
||||
const uid = await asyncMethodCall(
|
||||
$,
|
||||
{
|
||||
method: 'authenticate',
|
||||
params: [
|
||||
$.auth.data.databaseName,
|
||||
$.auth.data.email,
|
||||
$.auth.data.apiKey,
|
||||
[]
|
||||
]
|
||||
}
|
||||
);
|
||||
|
||||
if (!Number.isInteger(uid)) {
|
||||
// failed to authenticate
|
||||
throw new Error(
|
||||
'Failed to connect to the Odoo server. Please, check the credentials!'
|
||||
);
|
||||
}
|
||||
|
||||
return uid;
|
||||
}
|
0
packages/backend/src/apps/odoo/index.d.ts
vendored
Normal file
0
packages/backend/src/apps/odoo/index.d.ts
vendored
Normal file
16
packages/backend/src/apps/odoo/index.ts
Normal file
16
packages/backend/src/apps/odoo/index.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import defineApp from '../../helpers/define-app';
|
||||
import auth from './auth';
|
||||
import actions from './actions';
|
||||
|
||||
export default defineApp({
|
||||
name: 'Odoo',
|
||||
key: 'odoo',
|
||||
iconUrl: '{BASE_URL}/apps/odoo/assets/favicon.svg',
|
||||
authDocUrl: 'https://automatisch.io/docs/apps/odoo/connection',
|
||||
supportsConnections: true,
|
||||
baseUrl: 'https://odoo.com',
|
||||
apiBaseUrl: '',
|
||||
primaryColor: '9c5789',
|
||||
auth,
|
||||
actions
|
||||
});
|
@@ -102,6 +102,8 @@ export default defineAction({
|
||||
})
|
||||
.del() as IJSONArray;
|
||||
|
||||
client.destroy();
|
||||
|
||||
$.setActionItem({
|
||||
raw: {
|
||||
rows: response
|
||||
|
@@ -88,6 +88,8 @@ export default defineAction({
|
||||
.returning('*')
|
||||
.insert(data) as IJSONObject;
|
||||
|
||||
client.destroy();
|
||||
|
||||
$.setActionItem({ raw: response[0] as IJSONObject });
|
||||
},
|
||||
});
|
||||
|
@@ -46,6 +46,7 @@ export default defineAction({
|
||||
|
||||
const queryStatemnt = $.step.parameters.queryStatement;
|
||||
const { rows } = await client.raw(queryStatemnt);
|
||||
client.destroy();
|
||||
|
||||
$.setActionItem({
|
||||
raw: {
|
||||
|
@@ -132,6 +132,8 @@ export default defineAction({
|
||||
})
|
||||
.update(data) as IJSONArray;
|
||||
|
||||
client.destroy();
|
||||
|
||||
$.setActionItem({
|
||||
raw: {
|
||||
rows: response
|
||||
|
@@ -5,6 +5,7 @@ import getClient from '../common/postgres-client';
|
||||
const verifyCredentials = async ($: IGlobalVariable) => {
|
||||
const client = getClient($);
|
||||
const checkConnection = await client.raw('SELECT 1');
|
||||
client.destroy();
|
||||
|
||||
logger.debug(checkConnection);
|
||||
|
||||
|
@@ -11,8 +11,20 @@ const fetchMessages = async ($: IGlobalVariable) => {
|
||||
response = await $.http.get(requestPath);
|
||||
|
||||
response.data.messages.forEach((message: IJSONObject) => {
|
||||
const computedMessage = {
|
||||
To: message.to,
|
||||
Body: message.body,
|
||||
From: message.from,
|
||||
SmsSid: message.sid,
|
||||
NumMedia: message.num_media,
|
||||
SmsStatus: message.status,
|
||||
AccountSid: message.account_sid,
|
||||
ApiVersion: message.api_version,
|
||||
NumSegments: message.num_segments,
|
||||
};
|
||||
|
||||
const dataItem = {
|
||||
raw: message,
|
||||
raw: computedMessage,
|
||||
meta: {
|
||||
internalId: message.date_sent as string,
|
||||
},
|
||||
|
@@ -142,6 +142,15 @@ export default defineConfig({
|
||||
{ text: 'Connection', link: '/apps/http-request/connection' },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Mattermost',
|
||||
collapsible: true,
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'Actions', link: '/apps/mattermost/actions' },
|
||||
{ text: 'Connection', link: '/apps/mattermost/connection' },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Notion',
|
||||
collapsible: true,
|
||||
@@ -160,6 +169,15 @@ export default defineConfig({
|
||||
{ text: 'Connection', link: '/apps/ntfy/connection' },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Odoo',
|
||||
collapsible: true,
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'Actions', link: '/apps/odoo/actions' },
|
||||
{ text: 'Connection', link: '/apps/odoo/connection' },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'OpenAI',
|
||||
collapsible: true,
|
||||
|
@@ -2,13 +2,13 @@
|
||||
favicon: /favicons/github.svg
|
||||
items:
|
||||
- name: New issues
|
||||
desc: Triggers when a new issue is created
|
||||
desc: Triggers when a new issue is created.
|
||||
- name: New pull requests
|
||||
desc: Triggers when a new pull request is created
|
||||
desc: Triggers when a new pull request is created.
|
||||
- name: New stargazers
|
||||
desc: Triggers when a user stars a repository
|
||||
desc: Triggers when a user stars a repository.
|
||||
- name: New watchers
|
||||
desc: Triggers when a user watches a repository
|
||||
desc: Triggers when a user watches a repository.
|
||||
---
|
||||
|
||||
<script setup>
|
||||
|
@@ -2,31 +2,31 @@
|
||||
favicon: /favicons/gitlab.svg
|
||||
items:
|
||||
- name: Confidential issue event
|
||||
desc: Triggers when a new confidential issue is created or an existing issue is updated, closed, or reopened
|
||||
desc: Triggers when a new confidential issue is created or an existing issue is updated, closed, or reopened.
|
||||
- name: Confidential comment event
|
||||
desc: Triggers when a new confidential comment is made on commits, merge requests, issues, and code snippets
|
||||
desc: Triggers when a new confidential comment is made on commits, merge requests, issues, and code snippets.
|
||||
- name: Deployment event
|
||||
desc: Triggers when a deployment starts, succeeds, fails or is canceled
|
||||
desc: Triggers when a deployment starts, succeeds, fails or is canceled.
|
||||
- name: Feature flag event
|
||||
desc: Triggers when a feature flag is turned on or off
|
||||
desc: Triggers when a feature flag is turned on or off.
|
||||
- name: Issue event
|
||||
desc: Triggers when a new issue is created or an existing issue is updated, closed, or reopened
|
||||
desc: Triggers when a new issue is created or an existing issue is updated, closed, or reopened.
|
||||
- name: Job event
|
||||
desc: Triggers when the status of a job changes
|
||||
desc: Triggers when the status of a job changes.
|
||||
- name: Merge request event
|
||||
desc: Triggers when merge request is created, updated, or closed
|
||||
desc: Triggers when merge request is created, updated, or closed.
|
||||
- name: Comment event
|
||||
desc: Triggers when a new comment is made on commits, merge requests, issues, and code snippets
|
||||
desc: Triggers when a new comment is made on commits, merge requests, issues, and code snippets.
|
||||
- name: Pipeline event
|
||||
desc: Triggers when the status of a pipeline changes
|
||||
desc: Triggers when the status of a pipeline changes.
|
||||
- name: Push event
|
||||
desc: Triggers when you push to the repository
|
||||
desc: Triggers when you push to the repository.
|
||||
- name: Release event
|
||||
desc: Triggers when a release is created or updated
|
||||
desc: Triggers when a release is created or updated.
|
||||
- name: Tag event
|
||||
desc: Triggers when you create or delete tags in the repository
|
||||
desc: Triggers when you create or delete tags in the repository.
|
||||
- name: Wiki page event
|
||||
desc: Triggers when a wiki page is created, updated, or deleted
|
||||
desc: Triggers when a wiki page is created, updated, or deleted.
|
||||
---
|
||||
|
||||
<script setup>
|
||||
|
@@ -2,13 +2,13 @@
|
||||
favicon: /favicons/google-drive.svg
|
||||
items:
|
||||
- name: New files
|
||||
desc: Triggers when any new file is added (inside of any folder)
|
||||
desc: Triggers when any new file is added (inside of any folder).
|
||||
- name: New files in folder
|
||||
desc: Triggers when a new file is added directly to a specified folder (but not its subfolder)
|
||||
desc: Triggers when a new file is added directly to a specified folder (but not its subfolder).
|
||||
- name: New folders
|
||||
desc: Triggers when a new folder is added directly to a specified folder (but not its subfolder)
|
||||
desc: Triggers when a new folder is added directly to a specified folder (but not its subfolder).
|
||||
- name: Updated files
|
||||
desc: Triggers when a file is updated in a specified folder (but not its subfolder)
|
||||
desc: Triggers when a file is updated in a specified folder (but not its subfolder).
|
||||
---
|
||||
|
||||
<script setup>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
favicon: /favicons/google-forms.svg
|
||||
items:
|
||||
- name: New form responses
|
||||
desc: Triggers when a new form response is submitted
|
||||
desc: Triggers when a new form response is submitted.
|
||||
---
|
||||
|
||||
<script setup>
|
||||
|
@@ -1,8 +1,10 @@
|
||||
---
|
||||
favicon: /favicons/google-sheets.svg
|
||||
items:
|
||||
- name: Create Spreadsheet Row
|
||||
desc: Creates a new row in a specific spreadsheet
|
||||
- name: Create spreadsheet
|
||||
desc: Create a blank spreadsheet or duplicate an existing spreadsheet. Optionally, provide headers.
|
||||
- name: Create spreadsheet row
|
||||
desc: Creates a new row in a specific spreadsheet.
|
||||
---
|
||||
|
||||
<script setup>
|
||||
|
@@ -2,11 +2,11 @@
|
||||
favicon: /favicons/google-sheets.svg
|
||||
items:
|
||||
- name: New spreadsheets
|
||||
desc: Triggers when you create a new spreadsheet
|
||||
desc: Triggers when you create a new spreadsheet.
|
||||
- name: New worksheets
|
||||
desc: Triggers when you create a new worksheet in a spreadsheet
|
||||
desc: Triggers when you create a new worksheet in a spreadsheet.
|
||||
- name: New spreadsheet rows
|
||||
desc: Triggers when a new row is added to the bottom of a spreadsheet
|
||||
desc: Triggers when a new row is added to the bottom of a spreadsheet.
|
||||
---
|
||||
|
||||
<script setup>
|
||||
|
12
packages/docs/pages/apps/mattermost/actions.md
Normal file
12
packages/docs/pages/apps/mattermost/actions.md
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
favicon: /favicons/mattermost.svg
|
||||
items:
|
||||
- name: Send a message to channel
|
||||
desc: Sends a message to a channel you specify.
|
||||
---
|
||||
|
||||
<script setup>
|
||||
import CustomListing from '../../components/CustomListing.vue'
|
||||
</script>
|
||||
|
||||
<CustomListing />
|
19
packages/docs/pages/apps/mattermost/connection.md
Normal file
19
packages/docs/pages/apps/mattermost/connection.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Mattermost
|
||||
|
||||
:::info
|
||||
This page explains the steps you need to follow to set up the Mattermost
|
||||
connection in Automatisch. If any of the steps are outdated, please let us know!
|
||||
:::
|
||||
|
||||
1. Go to the `<WORKSPACE_NAME>/integrations/oauth2-apps/add` page of your Mattermost server to register a **new OAuth application**.
|
||||
- You can find details about registering new Mattermost oAuth application at https://docs.mattermost.com/integrations/cloud-oauth-2-0-applications.html#register-your-application-in-mattermost.
|
||||
2. Fill in the **Display Name** field.
|
||||
3. Fill in the **Description** field.
|
||||
4. Fill in the **Homepage** field.
|
||||
5. Copy **OAuth Redirect URL** from Automatisch to the **Callback URLs** field on Mattermost page.
|
||||
6. Click on the **Save** button at the end of the form on Mattermost page.
|
||||
7. Copy the **Client ID** value from the following page to the `Client ID` field on Automatisch.
|
||||
8. Copy the **Client Secret** value from the same page to the `Client Secret` field on Automatisch.
|
||||
9. Click **Done** button on MAttermost page.
|
||||
10. Click **Submit** button on Automatisch.
|
||||
11. Congrats! Start using your new Mattermost connection within the flows.
|
12
packages/docs/pages/apps/odoo/actions.md
Normal file
12
packages/docs/pages/apps/odoo/actions.md
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
favicon: /favicons/odoo.svg
|
||||
items:
|
||||
- name: Create a lead or opportunity
|
||||
desc: Creates a new CRM record as a lead or opportunity.
|
||||
---
|
||||
|
||||
<script setup>
|
||||
import CustomListing from '../../components/CustomListing.vue'
|
||||
</script>
|
||||
|
||||
<CustomListing />
|
16
packages/docs/pages/apps/odoo/connection.md
Normal file
16
packages/docs/pages/apps/odoo/connection.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Odoo
|
||||
|
||||
:::info
|
||||
This page explains the steps you need to follow to set up the Odoo
|
||||
connection in Automatisch. If any of the steps are outdated, please let us know!
|
||||
:::
|
||||
|
||||
To create a connection, you need to supply the following information:
|
||||
|
||||
1. Fill the **Host Name** field with the Odoo host.
|
||||
1. Fill the **Port** field with the Odoo port.
|
||||
1. Fill the **Database Name** field with the Odoo database.
|
||||
1. Fill the **Email Address** field with the email address of the account that will be intereacting with the database.
|
||||
1. Fill the **API Key** field with the API key for your Odoo account.
|
||||
|
||||
Odoo's [API documentation](https://www.odoo.com/documentation/latest/developer/reference/external_api.html#api-keys) explains how to create API keys.
|
@@ -8,7 +8,7 @@ items:
|
||||
- name: Delete
|
||||
desc: Delete rows found based on the given where clause entries.
|
||||
- name: SQL query
|
||||
desc: Executes the given SQL statement..
|
||||
desc: Executes the given SQL statement.
|
||||
---
|
||||
|
||||
<script setup>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
favicon: /favicons/signalwire.svg
|
||||
items:
|
||||
- name: Send an SMS
|
||||
desc: Sends an SMS
|
||||
desc: Sends an SMS.
|
||||
---
|
||||
|
||||
<script setup>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
favicon: /favicons/smtp.svg
|
||||
items:
|
||||
- name: Send an email
|
||||
desc: Sends an email
|
||||
desc: Sends an email.
|
||||
---
|
||||
|
||||
<script setup>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
favicon: /favicons/spotify.svg
|
||||
items:
|
||||
- name: Create playlist
|
||||
desc: Create a playlist on user's account
|
||||
desc: Create a playlist on user's account.
|
||||
---
|
||||
|
||||
<script setup>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
favicon: /favicons/twilio.svg
|
||||
items:
|
||||
- name: Send an SMS
|
||||
desc: Sends an SMS
|
||||
desc: Sends an SMS.
|
||||
---
|
||||
|
||||
<script setup>
|
||||
|
@@ -18,8 +18,10 @@ Following integrations are currently supported by Automatisch.
|
||||
- [Google Forms](/apps/google-forms/triggers)
|
||||
- [Google Sheets](/apps/google-sheets/triggers)
|
||||
- [HTTP Request](/apps/http-request/actions)
|
||||
- [Mattermost](/apps/mattermost/actions)
|
||||
- [Notion](/apps/notion/triggers)
|
||||
- [Ntfy](/apps/ntfy/actions)
|
||||
- [Odoo](/apps/odoo/actions)
|
||||
- [OpenAI](/apps/openai/actions)
|
||||
- [PostgreSQL](/apps/postgresql/actions)
|
||||
- [RSS](/apps/rss/triggers)
|
||||
|
6
packages/docs/pages/public/favicons/mattermost.svg
Normal file
6
packages/docs/pages/public/favicons/mattermost.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="256px" height="256px" viewBox="0 0 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
|
||||
<g>
|
||||
<path d="M6.79123171,86.9648684 C25.2351716,32.4823178 76.783459,-1.43234143 131.421839,0.0464773399 L131.421839,0.0464773399 L113.909757,20.739032 C81.4957329,26.5997669 53.5072568,48.7337413 42.5072761,81.2287969 C26.140539,129.576353 53.572705,182.545803 103.779303,199.543648 C153.985902,216.538377 207.952658,191.12264 224.319395,142.7782 C235.283535,110.390667 226.589826,75.9306053 204.563374,51.5978814 L204.563374,51.5978814 L203.21701,24.4290666 C247.371203,56.4768925 267.622761,114.633895 249.208429,169.029181 C226.546194,235.970273 153.909545,271.865521 86.9684532,249.204844 C20.0273609,226.542609 -15.8694453,153.905961 6.79123171,86.9648684 Z M165.185344,11.9237762 C165.839826,11.6401671 166.594039,11.5793938 167.321762,11.8256038 C168.035459,12.0671391 168.585536,12.5580009 168.936152,13.1595015 L168.936152,13.1595015 L169.007833,13.2763734 L169.071723,13.4103864 C169.240019,13.7313945 169.383381,14.0991514 169.450388,14.5510559 C169.582343,15.4417519 169.641535,17.5358595 169.665634,19.6808502 L169.671365,20.2662434 C169.677102,20.9486534 169.679633,21.6256073 169.680171,22.2599793 L169.680173,22.7924325 C169.678741,24.5267431 169.663874,25.8268542 169.663874,25.8268542 L169.663874,25.8268542 L170.167202,44.7600977 L170.910507,66.6151379 L171.837691,104.59538 C171.837691,104.59538 171.83785,104.602367 171.838064,104.616156 L171.838772,104.677745 C171.838883,104.691349 171.838983,104.706608 171.839058,104.723498 L171.839105,104.844231 C171.832023,107.013302 171.387173,122.892918 160.122454,133.928662 C148.009853,145.795053 133.131285,144.708923 123.451177,141.433394 C113.771069,138.154749 101.293828,129.979951 98.8800345,113.195592 C96.8283098,98.9302108 104.41287,86.9390787 106.734401,83.6627102 L106.889339,83.4459953 C107.205256,83.0081712 107.389865,82.7777388 107.389865,82.7777388 L107.389865,82.7777388 L131.197445,53.1717559 L145.064682,36.2627333 L156.965355,21.5275276 C156.965355,21.5275276 158.715313,19.1834331 160.51647,16.874806 L160.876881,16.4142586 C161.477025,15.6498178 162.070275,14.9069442 162.593713,14.2737698 L162.898895,13.907734 C163.342593,13.3805415 163.71955,12.9564826 163.983901,12.6998055 C164.292443,12.4006135 164.608776,12.205827 164.918876,12.0546727 L164.918876,12.0546727 L165.146386,11.9393591 Z" fill="#0058CC"></path>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
1
packages/docs/pages/public/favicons/odoo.svg
Normal file
1
packages/docs/pages/public/favicons/odoo.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="191"><circle cx="527.5" cy="118.4" r="72.4" fill="#888"/><path d="M527.5 161.1c23.6 0 42.7-19.1 42.7-42.7s-19.1-42.7-42.7-42.7-42.7 19.1-42.7 42.7 19.1 42.7 42.7 42.7z" fill="#fff"/><circle cx="374" cy="118.4" r="72.4" fill="#888"/><path d="M374 161.1c23.6 0 42.7-19.1 42.7-42.7S397.6 75.7 374 75.7s-42.7 19.1-42.7 42.7 19.1 42.7 42.7 42.7z" fill="#fff"/><path d="M294.9 117.8v.6c0 40-32.4 72.4-72.4 72.4s-72.4-32.4-72.4-72.4S182.5 46 222.5 46c16.4 0 31.5 5.5 43.7 14.6V14.4A14.34 14.34 0 0 1 280.6 0c7.9 0 14.4 6.5 14.4 14.4v102.7c0 .2 0 .5-.1.7z" fill="#888"/><circle cx="222.5" cy="118.4" r="42.7" fill="#fff"/><circle cx="72.4" cy="118.2" r="72.4" fill="#9c5789"/><circle cx="71.7" cy="118.5" r="42.7" fill="#fff"/><script xmlns=""/></svg>
|
After Width: | Height: | Size: 803 B |
28
yarn.lock
28
yarn.lock
@@ -4328,6 +4328,13 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/xmlrpc@^1.3.7":
|
||||
version "1.3.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/xmlrpc/-/xmlrpc-1.3.7.tgz#a95e8636fe9b848772088cfaa8021d0ad0ad99a0"
|
||||
integrity sha512-T+jYEZz/dJvI40dkqx/FNNkyyWDyOb0HgQDpni48r4NyB8n7xjKFDACi8O3NkAWz5cLWEmKRzWfzCEZ5EB6CVg==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/yargs-parser@*":
|
||||
version "20.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129"
|
||||
@@ -8841,9 +8848,9 @@ fast-redact@^3.0.0:
|
||||
integrity sha512-YN+CYfCVRVMUZOUPeinHNKgytM1wPI/C/UCLEi56EsY2dwwvI00kIJHJoI7pMVqGoMew8SMZ2SSfHKHULHXDsg==
|
||||
|
||||
fast-xml-parser@^4.0.11:
|
||||
version "4.2.4"
|
||||
resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.2.4.tgz#6e846ede1e56ad9e5ef07d8720809edf0ed07e9b"
|
||||
integrity sha512-fbfMDvgBNIdDJLdLOwacjFAPYt67tr31H9ZhWSm45CDAxvd0I6WTlSOUo7K2P/K5sA5JgMKG64PI3DMcaFdWpQ==
|
||||
version "4.2.5"
|
||||
resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz#a6747a09296a6cb34f2ae634019bf1738f3b421f"
|
||||
integrity sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==
|
||||
dependencies:
|
||||
strnum "^1.0.5"
|
||||
|
||||
@@ -15412,7 +15419,7 @@ sax@1.2.1:
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a"
|
||||
integrity sha1-e45lYZCyKOgaZq6nSEgNgozS03o=
|
||||
|
||||
sax@>=0.6.0, sax@^1.2.4, sax@~1.2.4:
|
||||
sax@1.2.x, sax@>=0.6.0, sax@^1.2.4, sax@~1.2.4:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
||||
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
|
||||
@@ -17982,6 +17989,11 @@ xml2js@0.4.19:
|
||||
sax ">=0.6.0"
|
||||
xmlbuilder "~9.0.1"
|
||||
|
||||
xmlbuilder@8.2.x:
|
||||
version "8.2.2"
|
||||
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-8.2.2.tgz#69248673410b4ba42e1a6136551d2922335aa773"
|
||||
integrity sha512-eKRAFz04jghooy8muekqzo8uCSVNeyRedbuJrp0fovbLIi7wlsYtdUn3vBAAPq2Y3/0xMz2WMEUQ8yhVVO9Stw==
|
||||
|
||||
xmlbuilder@~9.0.1:
|
||||
version "9.0.7"
|
||||
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
|
||||
@@ -17992,6 +18004,14 @@ xmlchars@^2.2.0:
|
||||
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
|
||||
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
|
||||
|
||||
xmlrpc@^1.3.2:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/xmlrpc/-/xmlrpc-1.3.2.tgz#26b2ea347848d028aac7e7514b5351976de3e83d"
|
||||
integrity sha512-jQf5gbrP6wvzN71fgkcPPkF4bF/Wyovd7Xdff8d6/ihxYmgETQYSuTc+Hl+tsh/jmgPLro/Aro48LMFlIyEKKQ==
|
||||
dependencies:
|
||||
sax "1.2.x"
|
||||
xmlbuilder "8.2.x"
|
||||
|
||||
xtend@^4.0.0, xtend@^4.0.2, xtend@~4.0.1:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||
|
Reference in New Issue
Block a user