Compare commits
	
		
			29 Commits
		
	
	
		
			dependabot
			...
			AUT-758
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					01472c1fb9 | ||
| 
						 | 
					703e434e5c | ||
| 
						 | 
					c12dacfa40 | ||
| 
						 | 
					71a404063c | ||
| 
						 | 
					8bb7b16c0e | ||
| 
						 | 
					d7893d9a32 | ||
| 
						 | 
					9cbdda330c | ||
| 
						 | 
					42a9bfd099 | ||
| 
						 | 
					eb15bd01ca | ||
| 
						 | 
					9e98aebeb3 | ||
| 
						 | 
					1361cbc826 | ||
| 
						 | 
					679d0808a9 | ||
| 
						 | 
					6fe9a548ad | ||
| 
						 | 
					2d6d2430d2 | ||
| 
						 | 
					a445538e81 | ||
| 
						 | 
					50d38ffbd8 | ||
| 
						 | 
					93bcdfd9c9 | ||
| 
						 | 
					5be3b101a5 | ||
| 
						 | 
					024c7476c7 | ||
| 
						 | 
					30a7ffe93d | ||
| 
						 | 
					e2d803ebf7 | ||
| 
						 | 
					be7e67c940 | ||
| 
						 | 
					ead4b13ba5 | ||
| 
						 | 
					e02c42ee18 | ||
| 
						 | 
					d39886fdf8 | ||
| 
						 | 
					11a425f1de | ||
| 
						 | 
					37e1acc5f1 | ||
| 
						 | 
					ffaf6a577d | ||
| 
						 | 
					afdaf6ba39 | 
@@ -8,7 +8,7 @@
 | 
			
		||||
      "version": "latest"
 | 
			
		||||
    },
 | 
			
		||||
    "ghcr.io/devcontainers/features/node:1": {
 | 
			
		||||
      "version": 20
 | 
			
		||||
      "version": 18
 | 
			
		||||
    },
 | 
			
		||||
    "ghcr.io/devcontainers/features/common-utils:1": {
 | 
			
		||||
      "username": "vscode",
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,29 @@
 | 
			
		||||
import defineAction from '../../../../helpers/define-action.js';
 | 
			
		||||
 | 
			
		||||
export default defineAction({
 | 
			
		||||
  name: 'Delete document',
 | 
			
		||||
  key: 'deleteDocument',
 | 
			
		||||
  description: 'Deletes a document.',
 | 
			
		||||
  arguments: [
 | 
			
		||||
    {
 | 
			
		||||
      label: 'Document ID',
 | 
			
		||||
      key: 'documentId',
 | 
			
		||||
      type: 'string',
 | 
			
		||||
      required: true,
 | 
			
		||||
      description: '',
 | 
			
		||||
      variables: true,
 | 
			
		||||
    },
 | 
			
		||||
  ],
 | 
			
		||||
 | 
			
		||||
  async run($) {
 | 
			
		||||
    const { documentId } = $.step.parameters;
 | 
			
		||||
 | 
			
		||||
    await $.http.delete(`/v1/documents/${documentId}`);
 | 
			
		||||
 | 
			
		||||
    $.setActionItem({
 | 
			
		||||
      raw: {
 | 
			
		||||
        result: 'successful',
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
@@ -0,0 +1,27 @@
 | 
			
		||||
import defineAction from '../../../../helpers/define-action.js';
 | 
			
		||||
 | 
			
		||||
export default defineAction({
 | 
			
		||||
  name: 'Find document',
 | 
			
		||||
  key: 'findDocument',
 | 
			
		||||
  description: 'Finds a document.',
 | 
			
		||||
  arguments: [
 | 
			
		||||
    {
 | 
			
		||||
      label: 'Document ID',
 | 
			
		||||
      key: 'documentId',
 | 
			
		||||
      type: 'string',
 | 
			
		||||
      required: true,
 | 
			
		||||
      description: '',
 | 
			
		||||
      variables: true,
 | 
			
		||||
    },
 | 
			
		||||
  ],
 | 
			
		||||
 | 
			
		||||
  async run($) {
 | 
			
		||||
    const { documentId } = $.step.parameters;
 | 
			
		||||
 | 
			
		||||
    const { data } = await $.http.get(`/v1/documents/${documentId}`);
 | 
			
		||||
 | 
			
		||||
    $.setActionItem({
 | 
			
		||||
      raw: data.document,
 | 
			
		||||
    });
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
@@ -0,0 +1,241 @@
 | 
			
		||||
import defineAction from '../../../../helpers/define-action.js';
 | 
			
		||||
 | 
			
		||||
export default defineAction({
 | 
			
		||||
  name: 'Generate document',
 | 
			
		||||
  key: 'generateDocument',
 | 
			
		||||
  description: 'Creates a new document.',
 | 
			
		||||
  arguments: [
 | 
			
		||||
    {
 | 
			
		||||
      label: 'Workspace',
 | 
			
		||||
      key: 'workspaceId',
 | 
			
		||||
      type: 'dropdown',
 | 
			
		||||
      required: true,
 | 
			
		||||
      description: '',
 | 
			
		||||
      variables: true,
 | 
			
		||||
      source: {
 | 
			
		||||
        type: 'query',
 | 
			
		||||
        name: 'getDynamicData',
 | 
			
		||||
        arguments: [
 | 
			
		||||
          {
 | 
			
		||||
            name: 'key',
 | 
			
		||||
            value: 'listWorkspaces',
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      label: 'Template',
 | 
			
		||||
      key: 'templateId',
 | 
			
		||||
      type: 'dropdown',
 | 
			
		||||
      required: false,
 | 
			
		||||
      depensOn: ['parameters.workspaceId'],
 | 
			
		||||
      description: '',
 | 
			
		||||
      variables: true,
 | 
			
		||||
      source: {
 | 
			
		||||
        type: 'query',
 | 
			
		||||
        name: 'getDynamicData',
 | 
			
		||||
        arguments: [
 | 
			
		||||
          {
 | 
			
		||||
            name: 'key',
 | 
			
		||||
            value: 'listTemplates',
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            name: 'parameters.workspaceId',
 | 
			
		||||
            value: '{parameters.workspaceId}',
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      label: 'Use a Custom Json Structure?',
 | 
			
		||||
      key: 'useCustomJsonStructure',
 | 
			
		||||
      type: 'dropdown',
 | 
			
		||||
      required: true,
 | 
			
		||||
      description:
 | 
			
		||||
        'Please indicate "yes" if you would rather create a full JSON payload instead of relying on Automatisch mapping for the Document data.',
 | 
			
		||||
      variables: true,
 | 
			
		||||
      options: [
 | 
			
		||||
        {
 | 
			
		||||
          label: 'Yes',
 | 
			
		||||
          value: true,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          label: 'No',
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
      additionalFields: {
 | 
			
		||||
        type: 'query',
 | 
			
		||||
        name: 'getDynamicFields',
 | 
			
		||||
        arguments: [
 | 
			
		||||
          {
 | 
			
		||||
            name: 'key',
 | 
			
		||||
            value: 'listDocumentData',
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            name: 'parameters.useCustomJsonStructure',
 | 
			
		||||
            value: '{parameters.useCustomJsonStructure}',
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      label: 'Add Line Items?',
 | 
			
		||||
      key: 'addLineItems',
 | 
			
		||||
      type: 'dropdown',
 | 
			
		||||
      required: true,
 | 
			
		||||
      description:
 | 
			
		||||
        'Choose "yes" to include information for Line Items (such as in an invoice).',
 | 
			
		||||
      variables: true,
 | 
			
		||||
      options: [
 | 
			
		||||
        {
 | 
			
		||||
          label: 'Yes',
 | 
			
		||||
          value: true,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          label: 'No',
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
      additionalFields: {
 | 
			
		||||
        type: 'query',
 | 
			
		||||
        name: 'getDynamicFields',
 | 
			
		||||
        arguments: [
 | 
			
		||||
          {
 | 
			
		||||
            name: 'key',
 | 
			
		||||
            value: 'listLineItems',
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            name: 'parameters.addLineItems',
 | 
			
		||||
            value: '{parameters.addLineItems}',
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      label: 'Custom Filename',
 | 
			
		||||
      key: 'customFilename',
 | 
			
		||||
      type: 'string',
 | 
			
		||||
      required: false,
 | 
			
		||||
      description:
 | 
			
		||||
        'You have the option to define a custom filename for generated documents. If left blank, a random value will be assigned.',
 | 
			
		||||
      variables: true,
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      label: 'Meta Data',
 | 
			
		||||
      key: 'metaData',
 | 
			
		||||
      type: 'dynamic',
 | 
			
		||||
      required: false,
 | 
			
		||||
      description:
 | 
			
		||||
        'Extra information appended to the generated Document but not accessible within its Template.',
 | 
			
		||||
      fields: [
 | 
			
		||||
        {
 | 
			
		||||
          label: 'Key',
 | 
			
		||||
          key: 'metaDataKey',
 | 
			
		||||
          type: 'string',
 | 
			
		||||
          required: false,
 | 
			
		||||
          description: '',
 | 
			
		||||
          variables: true,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          label: 'Value',
 | 
			
		||||
          key: 'metaDataValue',
 | 
			
		||||
          type: 'string',
 | 
			
		||||
          required: false,
 | 
			
		||||
          description: '',
 | 
			
		||||
          variables: true,
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
  ],
 | 
			
		||||
 | 
			
		||||
  async run($) {
 | 
			
		||||
    const {
 | 
			
		||||
      templateId,
 | 
			
		||||
      useCustomJsonStructure,
 | 
			
		||||
      customJsonPayload,
 | 
			
		||||
      customFilename,
 | 
			
		||||
      addLineItems,
 | 
			
		||||
      lineItems,
 | 
			
		||||
      documentData,
 | 
			
		||||
      metaData,
 | 
			
		||||
    } = $.step.parameters;
 | 
			
		||||
    let payload = {};
 | 
			
		||||
    let meta = {};
 | 
			
		||||
 | 
			
		||||
    const documentDataObject = documentData.reduce((result, entry) => {
 | 
			
		||||
      const key = entry.documentDataKey?.toLowerCase();
 | 
			
		||||
      const value = entry.documentDataValue;
 | 
			
		||||
 | 
			
		||||
      if (key && value) {
 | 
			
		||||
        return {
 | 
			
		||||
          ...result,
 | 
			
		||||
          [entry.documentDataKey?.toLowerCase()]: entry.documentDataValue,
 | 
			
		||||
        };
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return result;
 | 
			
		||||
    }, {});
 | 
			
		||||
 | 
			
		||||
    const lineItemsObject = lineItems.reduce((result, entry) => {
 | 
			
		||||
      const key = entry.lineItemKey?.toLowerCase();
 | 
			
		||||
      const value = entry.lineItemValue;
 | 
			
		||||
 | 
			
		||||
      if (key && value) {
 | 
			
		||||
        return {
 | 
			
		||||
          ...result,
 | 
			
		||||
          [entry.lineItemKey?.toLowerCase()]: entry.lineItemValue,
 | 
			
		||||
        };
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return result;
 | 
			
		||||
    }, {});
 | 
			
		||||
 | 
			
		||||
    const metaDataObject = metaData.reduce((result, entry) => {
 | 
			
		||||
      const key = entry.metaDataKey?.toLowerCase();
 | 
			
		||||
      const value = entry.metaDataValue;
 | 
			
		||||
 | 
			
		||||
      if (key && value) {
 | 
			
		||||
        return {
 | 
			
		||||
          ...result,
 | 
			
		||||
          [entry.metaDataKey?.toLowerCase()]: entry.metaDataValue,
 | 
			
		||||
        };
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return result;
 | 
			
		||||
    }, {});
 | 
			
		||||
 | 
			
		||||
    if (metaDataObject) {
 | 
			
		||||
      meta = metaDataObject;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (customFilename) {
 | 
			
		||||
      meta._filename = customFilename;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (useCustomJsonStructure) {
 | 
			
		||||
      payload = JSON.parse(customJsonPayload);
 | 
			
		||||
    } else {
 | 
			
		||||
      payload = documentDataObject;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (addLineItems) {
 | 
			
		||||
      payload.lineItems = [lineItemsObject];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const body = {
 | 
			
		||||
      document: {
 | 
			
		||||
        document_template_id: templateId,
 | 
			
		||||
        meta: JSON.stringify(meta),
 | 
			
		||||
        payload: JSON.stringify(payload),
 | 
			
		||||
        status: 'pending',
 | 
			
		||||
      },
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const { data } = await $.http.post('/v1/documents', body);
 | 
			
		||||
 | 
			
		||||
    $.setActionItem({
 | 
			
		||||
      raw: data,
 | 
			
		||||
    });
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										5
									
								
								packages/backend/src/apps/pdf-monkey/actions/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								packages/backend/src/apps/pdf-monkey/actions/index.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
import deleteDocument from './delete-document/index.js';
 | 
			
		||||
import findDocument from './find-document/index.js';
 | 
			
		||||
import generateDocument from './generate-document/index.js';
 | 
			
		||||
 | 
			
		||||
export default [deleteDocument, findDocument, generateDocument];
 | 
			
		||||
							
								
								
									
										1487
									
								
								packages/backend/src/apps/pdf-monkey/assets/favicon.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1487
									
								
								packages/backend/src/apps/pdf-monkey/assets/favicon.svg
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
		 After Width: | Height: | Size: 112 KiB  | 
							
								
								
									
										21
									
								
								packages/backend/src/apps/pdf-monkey/auth/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								packages/backend/src/apps/pdf-monkey/auth/index.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
import verifyCredentials from './verify-credentials.js';
 | 
			
		||||
import isStillVerified from './is-still-verified.js';
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  fields: [
 | 
			
		||||
    {
 | 
			
		||||
      key: 'apiKey',
 | 
			
		||||
      label: 'API Key',
 | 
			
		||||
      type: 'string',
 | 
			
		||||
      required: true,
 | 
			
		||||
      readOnly: false,
 | 
			
		||||
      value: null,
 | 
			
		||||
      placeholder: null,
 | 
			
		||||
      description: 'PDFMonkey API secret key of your account.',
 | 
			
		||||
      clickToCopy: false,
 | 
			
		||||
    },
 | 
			
		||||
  ],
 | 
			
		||||
 | 
			
		||||
  verifyCredentials,
 | 
			
		||||
  isStillVerified,
 | 
			
		||||
};
 | 
			
		||||
@@ -0,0 +1,8 @@
 | 
			
		||||
import getCurrentUser from '../common/get-current-user.js';
 | 
			
		||||
 | 
			
		||||
const isStillVerified = async ($) => {
 | 
			
		||||
  const currentUser = await getCurrentUser($);
 | 
			
		||||
  return !!currentUser.id;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default isStillVerified;
 | 
			
		||||
@@ -0,0 +1,15 @@
 | 
			
		||||
import getCurrentUser from '../common/get-current-user.js';
 | 
			
		||||
 | 
			
		||||
const verifyCredentials = async ($) => {
 | 
			
		||||
  const currentUser = await getCurrentUser($);
 | 
			
		||||
  const screenName = [currentUser.desired_name, currentUser.email]
 | 
			
		||||
    .filter(Boolean)
 | 
			
		||||
    .join(' @ ');
 | 
			
		||||
 | 
			
		||||
  await $.auth.set({
 | 
			
		||||
    screenName,
 | 
			
		||||
    apiKey: $.auth.data.apiKey,
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default verifyCredentials;
 | 
			
		||||
@@ -0,0 +1,9 @@
 | 
			
		||||
const addAuthHeader = ($, requestConfig) => {
 | 
			
		||||
  if ($.auth.data?.apiKey) {
 | 
			
		||||
    requestConfig.headers.Authorization = `Bearer ${$.auth.data.apiKey}`;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return requestConfig;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default addAuthHeader;
 | 
			
		||||
@@ -0,0 +1,8 @@
 | 
			
		||||
const getCurrentUser = async ($) => {
 | 
			
		||||
  const response = await $.http.get('/v1/current_user');
 | 
			
		||||
  const currentUser = response.data.current_user;
 | 
			
		||||
 | 
			
		||||
  return currentUser;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default getCurrentUser;
 | 
			
		||||
@@ -0,0 +1,4 @@
 | 
			
		||||
import listTemplates from './list-templates/index.js';
 | 
			
		||||
import listWorkspaces from './list-workspaces/index.js';
 | 
			
		||||
 | 
			
		||||
export default [listTemplates, listWorkspaces];
 | 
			
		||||
@@ -0,0 +1,39 @@
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'List templates',
 | 
			
		||||
  key: 'listTemplates',
 | 
			
		||||
 | 
			
		||||
  async run($) {
 | 
			
		||||
    const templates = {
 | 
			
		||||
      data: [],
 | 
			
		||||
    };
 | 
			
		||||
    const workspaceId = $.step.parameters.workspaceId;
 | 
			
		||||
    let next = false;
 | 
			
		||||
 | 
			
		||||
    const params = {
 | 
			
		||||
      page: 'all',
 | 
			
		||||
      'q[workspace_id]': workspaceId,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (!workspaceId) {
 | 
			
		||||
      return templates;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    do {
 | 
			
		||||
      const { data } = await $.http.get('/v1/document_template_cards', params);
 | 
			
		||||
      next = data.meta.next_page;
 | 
			
		||||
 | 
			
		||||
      if (!data?.document_template_cards?.length) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      for (const template of data.document_template_cards) {
 | 
			
		||||
        templates.data.push({
 | 
			
		||||
          value: template.id,
 | 
			
		||||
          name: template.identifier,
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    } while (next);
 | 
			
		||||
 | 
			
		||||
    return templates;
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
@@ -0,0 +1,29 @@
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'List workspaces',
 | 
			
		||||
  key: 'listWorkspaces',
 | 
			
		||||
 | 
			
		||||
  async run($) {
 | 
			
		||||
    const workspaces = {
 | 
			
		||||
      data: [],
 | 
			
		||||
    };
 | 
			
		||||
    let next = false;
 | 
			
		||||
 | 
			
		||||
    do {
 | 
			
		||||
      const { data } = await $.http.get('/v1/workspace_cards');
 | 
			
		||||
      next = data.meta.next_page;
 | 
			
		||||
 | 
			
		||||
      if (!data?.workspace_cards?.length) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      for (const workspace of data.workspace_cards) {
 | 
			
		||||
        workspaces.data.push({
 | 
			
		||||
          value: workspace.id,
 | 
			
		||||
          name: workspace.identifier,
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    } while (next);
 | 
			
		||||
 | 
			
		||||
    return workspaces;
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
@@ -0,0 +1,4 @@
 | 
			
		||||
import listDocumentData from './list-document-data/index.js';
 | 
			
		||||
import listLineItems from './list-line-items/index.js';
 | 
			
		||||
 | 
			
		||||
export default [listDocumentData, listLineItems];
 | 
			
		||||
@@ -0,0 +1,48 @@
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'List document data',
 | 
			
		||||
  key: 'listDocumentData',
 | 
			
		||||
 | 
			
		||||
  async run($) {
 | 
			
		||||
    if ($.step.parameters.useCustomJsonStructure) {
 | 
			
		||||
      return [
 | 
			
		||||
        {
 | 
			
		||||
          label: 'Data for the Document (JSON Payload)',
 | 
			
		||||
          key: 'customJsonPayload',
 | 
			
		||||
          type: 'string',
 | 
			
		||||
          required: false,
 | 
			
		||||
          description:
 | 
			
		||||
            'Use the JSON format { "firstname": "John", "lastname": "Doe" }.',
 | 
			
		||||
          variables: true,
 | 
			
		||||
        },
 | 
			
		||||
      ];
 | 
			
		||||
    } else {
 | 
			
		||||
      return [
 | 
			
		||||
        {
 | 
			
		||||
          label: 'Data for the Document',
 | 
			
		||||
          key: 'documentData',
 | 
			
		||||
          type: 'dynamic',
 | 
			
		||||
          required: false,
 | 
			
		||||
          description: '',
 | 
			
		||||
          fields: [
 | 
			
		||||
            {
 | 
			
		||||
              label: 'Key',
 | 
			
		||||
              key: 'documentDataKey',
 | 
			
		||||
              type: 'string',
 | 
			
		||||
              required: false,
 | 
			
		||||
              description: '',
 | 
			
		||||
              variables: true,
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              label: 'Value',
 | 
			
		||||
              key: 'documentDataValue',
 | 
			
		||||
              type: 'string',
 | 
			
		||||
              required: false,
 | 
			
		||||
              description: '',
 | 
			
		||||
              variables: true,
 | 
			
		||||
            },
 | 
			
		||||
          ],
 | 
			
		||||
        },
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
@@ -0,0 +1,37 @@
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'List line items',
 | 
			
		||||
  key: 'listLineItems',
 | 
			
		||||
 | 
			
		||||
  async run($) {
 | 
			
		||||
    if ($.step.parameters.addLineItems) {
 | 
			
		||||
      return [
 | 
			
		||||
        {
 | 
			
		||||
          label: 'Line Items',
 | 
			
		||||
          key: 'lineItems',
 | 
			
		||||
          type: 'dynamic',
 | 
			
		||||
          required: false,
 | 
			
		||||
          description:
 | 
			
		||||
            'Data for a single item. Available as "lineItems" in your PDFMonkey Template.',
 | 
			
		||||
          fields: [
 | 
			
		||||
            {
 | 
			
		||||
              label: 'Key',
 | 
			
		||||
              key: 'lineItemKey',
 | 
			
		||||
              type: 'string',
 | 
			
		||||
              required: false,
 | 
			
		||||
              description: '',
 | 
			
		||||
              variables: true,
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              label: 'Value',
 | 
			
		||||
              key: 'lineItemValue',
 | 
			
		||||
              type: 'string',
 | 
			
		||||
              required: false,
 | 
			
		||||
              description: '',
 | 
			
		||||
              variables: true,
 | 
			
		||||
            },
 | 
			
		||||
          ],
 | 
			
		||||
        },
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										24
									
								
								packages/backend/src/apps/pdf-monkey/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								packages/backend/src/apps/pdf-monkey/index.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
import defineApp from '../../helpers/define-app.js';
 | 
			
		||||
import addAuthHeader from './common/add-auth-header.js';
 | 
			
		||||
import auth from './auth/index.js';
 | 
			
		||||
import triggers from './triggers/index.js';
 | 
			
		||||
import dynamicData from './dynamic-data/index.js';
 | 
			
		||||
import actions from './actions/index.js';
 | 
			
		||||
import dynamicFields from './dynamic-fields/index.js';
 | 
			
		||||
 | 
			
		||||
export default defineApp({
 | 
			
		||||
  name: 'PDFMonkey',
 | 
			
		||||
  key: 'pdf-monkey',
 | 
			
		||||
  iconUrl: '{BASE_URL}/apps/pdf-monkey/assets/favicon.svg',
 | 
			
		||||
  authDocUrl: 'https://automatisch.io/docs/apps/pdf-monkey/connection',
 | 
			
		||||
  supportsConnections: true,
 | 
			
		||||
  baseUrl: 'https://pdfmonkey.io',
 | 
			
		||||
  apiBaseUrl: 'https://api.pdfmonkey.io/api',
 | 
			
		||||
  primaryColor: '376794',
 | 
			
		||||
  beforeRequest: [addAuthHeader],
 | 
			
		||||
  auth,
 | 
			
		||||
  triggers,
 | 
			
		||||
  dynamicData,
 | 
			
		||||
  actions,
 | 
			
		||||
  dynamicFields,
 | 
			
		||||
});
 | 
			
		||||
@@ -0,0 +1,99 @@
 | 
			
		||||
import defineTrigger from '../../../../helpers/define-trigger.js';
 | 
			
		||||
 | 
			
		||||
export default defineTrigger({
 | 
			
		||||
  name: 'Documents Generated',
 | 
			
		||||
  key: 'documentsGenerated',
 | 
			
		||||
  pollInterval: 15,
 | 
			
		||||
  description:
 | 
			
		||||
    'Triggers upon the successful completion of document generation.',
 | 
			
		||||
  arguments: [
 | 
			
		||||
    {
 | 
			
		||||
      label: 'Workspace',
 | 
			
		||||
      key: 'workspaceId',
 | 
			
		||||
      type: 'dropdown',
 | 
			
		||||
      required: true,
 | 
			
		||||
      description: '',
 | 
			
		||||
      variables: true,
 | 
			
		||||
      source: {
 | 
			
		||||
        type: 'query',
 | 
			
		||||
        name: 'getDynamicData',
 | 
			
		||||
        arguments: [
 | 
			
		||||
          {
 | 
			
		||||
            name: 'key',
 | 
			
		||||
            value: 'listWorkspaces',
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      label: 'Templates',
 | 
			
		||||
      key: 'templateIds',
 | 
			
		||||
      type: 'dynamic',
 | 
			
		||||
      required: false,
 | 
			
		||||
      description: 'Apply this trigger exclusively for particular templates.',
 | 
			
		||||
      fields: [
 | 
			
		||||
        {
 | 
			
		||||
          label: 'Template',
 | 
			
		||||
          key: 'templateId',
 | 
			
		||||
          type: 'dropdown',
 | 
			
		||||
          required: false,
 | 
			
		||||
          depensOn: ['parameters.workspaceId'],
 | 
			
		||||
          description: '',
 | 
			
		||||
          variables: true,
 | 
			
		||||
          source: {
 | 
			
		||||
            type: 'query',
 | 
			
		||||
            name: 'getDynamicData',
 | 
			
		||||
            arguments: [
 | 
			
		||||
              {
 | 
			
		||||
                name: 'key',
 | 
			
		||||
                value: 'listTemplates',
 | 
			
		||||
              },
 | 
			
		||||
              {
 | 
			
		||||
                name: 'parameters.workspaceId',
 | 
			
		||||
                value: '{parameters.workspaceId}',
 | 
			
		||||
              },
 | 
			
		||||
            ],
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
  ],
 | 
			
		||||
 | 
			
		||||
  async run($) {
 | 
			
		||||
    const workspaceId = $.step.parameters.workspaceId;
 | 
			
		||||
    const templateIds = $.step.parameters.templateIds;
 | 
			
		||||
    const allTemplates = templateIds
 | 
			
		||||
      .map((templateId) => templateId.templateId)
 | 
			
		||||
      .join(',');
 | 
			
		||||
 | 
			
		||||
    const params = {
 | 
			
		||||
      'page[size]': 100,
 | 
			
		||||
      'q[workspace_id]': workspaceId,
 | 
			
		||||
      'q[status]': 'success',
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (!templateIds.length) {
 | 
			
		||||
      params['q[document_template_id]'] = allTemplates;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let next = false;
 | 
			
		||||
    do {
 | 
			
		||||
      const { data } = await $.http.get('/v1/document_cards', { params });
 | 
			
		||||
 | 
			
		||||
      if (!data?.document_cards?.length) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      next = data.meta.next_page;
 | 
			
		||||
 | 
			
		||||
      for (const document of data.document_cards) {
 | 
			
		||||
        $.pushTriggerItem({
 | 
			
		||||
          raw: document,
 | 
			
		||||
          meta: {
 | 
			
		||||
            internalId: document.id,
 | 
			
		||||
          },
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    } while (next);
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										3
									
								
								packages/backend/src/apps/pdf-monkey/triggers/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								packages/backend/src/apps/pdf-monkey/triggers/index.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
import documentsGenerated from './documents-generated/index.js';
 | 
			
		||||
 | 
			
		||||
export default [documentsGenerated];
 | 
			
		||||
@@ -0,0 +1,10 @@
 | 
			
		||||
import { renderObject } from '../../../../../helpers/renderer.js';
 | 
			
		||||
import SamlAuthProvider from '../../../../../models/saml-auth-provider.ee.js';
 | 
			
		||||
 | 
			
		||||
export default async (request, response) => {
 | 
			
		||||
  const samlAuthProvider = await SamlAuthProvider.query()
 | 
			
		||||
    .findById(request.params.samlAuthProviderId)
 | 
			
		||||
    .throwIfNotFound();
 | 
			
		||||
 | 
			
		||||
  renderObject(response, samlAuthProvider);
 | 
			
		||||
};
 | 
			
		||||
@@ -0,0 +1,34 @@
 | 
			
		||||
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 { createRole } from '../../../../../../test/factories/role.js';
 | 
			
		||||
import { createUser } from '../../../../../../test/factories/user.js';
 | 
			
		||||
import { createSamlAuthProvider } from '../../../../../../test/factories/saml-auth-provider.ee.js';
 | 
			
		||||
import getSamlAuthProviderMock from '../../../../../../test/mocks/rest/api/v1/saml-auth-providers/get-saml-auth-provider.ee.js';
 | 
			
		||||
import * as license from '../../../../../helpers/license.ee.js';
 | 
			
		||||
 | 
			
		||||
describe('GET /api/v1/admin/saml-auth-provider/:samlAuthProviderId', () => {
 | 
			
		||||
  let samlAuthProvider, currentUser, token;
 | 
			
		||||
 | 
			
		||||
  beforeEach(async () => {
 | 
			
		||||
    const role = await createRole({ key: 'admin' });
 | 
			
		||||
    currentUser = await createUser({ roleId: role.id });
 | 
			
		||||
    samlAuthProvider = await createSamlAuthProvider();
 | 
			
		||||
 | 
			
		||||
    token = createAuthTokenByUserId(currentUser.id);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should return saml auth provider with specified id', async () => {
 | 
			
		||||
    vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true);
 | 
			
		||||
 | 
			
		||||
    const response = await request(app)
 | 
			
		||||
      .get(`/api/v1/admin/saml-auth-providers/${samlAuthProvider.id}`)
 | 
			
		||||
      .set('Authorization', token)
 | 
			
		||||
      .expect(200);
 | 
			
		||||
 | 
			
		||||
    const expectedPayload = await getSamlAuthProviderMock(samlAuthProvider);
 | 
			
		||||
 | 
			
		||||
    expect(response.body).toEqual(expectedPayload);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
@@ -0,0 +1,11 @@
 | 
			
		||||
import { renderObject } from '../../../../../helpers/renderer.js';
 | 
			
		||||
import SamlAuthProvider from '../../../../../models/saml-auth-provider.ee.js';
 | 
			
		||||
 | 
			
		||||
export default async (request, response) => {
 | 
			
		||||
  const samlAuthProviders = await SamlAuthProvider.query().orderBy(
 | 
			
		||||
    'created_at',
 | 
			
		||||
    'desc'
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  renderObject(response, samlAuthProviders);
 | 
			
		||||
};
 | 
			
		||||
@@ -0,0 +1,39 @@
 | 
			
		||||
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 { createRole } from '../../../../../../test/factories/role.js';
 | 
			
		||||
import { createUser } from '../../../../../../test/factories/user.js';
 | 
			
		||||
import { createSamlAuthProvider } from '../../../../../../test/factories/saml-auth-provider.ee.js';
 | 
			
		||||
import getSamlAuthProvidersMock from '../../../../../../test/mocks/rest/api/v1/saml-auth-providers/get-saml-auth-providers.ee.js';
 | 
			
		||||
import * as license from '../../../../../helpers/license.ee.js';
 | 
			
		||||
 | 
			
		||||
describe('GET /api/v1/admin/saml-auth-providers', () => {
 | 
			
		||||
  let samlAuthProviderOne, samlAuthProviderTwo, currentUser, token;
 | 
			
		||||
 | 
			
		||||
  beforeEach(async () => {
 | 
			
		||||
    const role = await createRole({ key: 'admin' });
 | 
			
		||||
    currentUser = await createUser({ roleId: role.id });
 | 
			
		||||
 | 
			
		||||
    samlAuthProviderOne = await createSamlAuthProvider();
 | 
			
		||||
    samlAuthProviderTwo = await createSamlAuthProvider();
 | 
			
		||||
 | 
			
		||||
    token = createAuthTokenByUserId(currentUser.id);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should return saml auth providers', async () => {
 | 
			
		||||
    vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true);
 | 
			
		||||
 | 
			
		||||
    const response = await request(app)
 | 
			
		||||
      .get('/api/v1/admin/saml-auth-providers')
 | 
			
		||||
      .set('Authorization', token)
 | 
			
		||||
      .expect(200);
 | 
			
		||||
 | 
			
		||||
    const expectedPayload = await getSamlAuthProvidersMock([
 | 
			
		||||
      samlAuthProviderTwo,
 | 
			
		||||
      samlAuthProviderOne,
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    expect(response.body).toEqual(expectedPayload);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
@@ -1,16 +1,17 @@
 | 
			
		||||
const authorizationList = {
 | 
			
		||||
  '/api/v1/users/:userId': {
 | 
			
		||||
  'GET /api/v1/users/:userId': {
 | 
			
		||||
    action: 'read',
 | 
			
		||||
    subject: 'User',
 | 
			
		||||
  },
 | 
			
		||||
  '/api/v1/users/': {
 | 
			
		||||
  'GET /api/v1/users/': {
 | 
			
		||||
    action: 'read',
 | 
			
		||||
    subject: 'User',
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const authorizeUser = async (request, response, next) => {
 | 
			
		||||
  const currentRoute = request.baseUrl + request.route.path;
 | 
			
		||||
  const currentRoute =
 | 
			
		||||
    request.method + ' ' + request.baseUrl + request.route.path;
 | 
			
		||||
  const currentRouteRule = authorizationList[currentRoute];
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
@@ -20,3 +21,13 @@ export const authorizeUser = async (request, response, next) => {
 | 
			
		||||
    return response.status(403).end();
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const authorizeAdmin = async (request, response, next) => {
 | 
			
		||||
  const role = await request.currentUser.$relatedQuery('role');
 | 
			
		||||
 | 
			
		||||
  if (role?.isAdmin) {
 | 
			
		||||
    next();
 | 
			
		||||
  } else {
 | 
			
		||||
    return response.status(403).end();
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								packages/backend/src/helpers/check-is-enterprise.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								packages/backend/src/helpers/check-is-enterprise.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
import { hasValidLicense } from './license.ee.js';
 | 
			
		||||
 | 
			
		||||
export const checkIsEnterprise = async (request, response, next) => {
 | 
			
		||||
  if (await hasValidLicense()) {
 | 
			
		||||
    next();
 | 
			
		||||
  } else {
 | 
			
		||||
    return response.status(404).end();
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
@@ -4,8 +4,8 @@ import appConfig from '../config/app.js';
 | 
			
		||||
const levels = {
 | 
			
		||||
  error: 0,
 | 
			
		||||
  warn: 1,
 | 
			
		||||
  info: 2,
 | 
			
		||||
  http: 3,
 | 
			
		||||
  http: 2,
 | 
			
		||||
  info: 3,
 | 
			
		||||
  debug: 4,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,8 @@ const renderObject = (response, object) => {
 | 
			
		||||
  let data = isPaginated(object) ? object.records : object;
 | 
			
		||||
  const type = isPaginated(object)
 | 
			
		||||
    ? object.records[0].constructor.name
 | 
			
		||||
    : Array.isArray(object)
 | 
			
		||||
    ? object[0].constructor.name
 | 
			
		||||
    : object.constructor.name;
 | 
			
		||||
 | 
			
		||||
  const serializer = serializers[type];
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										26
									
								
								packages/backend/src/routes/api/v1/saml-auth-providers.ee.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								packages/backend/src/routes/api/v1/saml-auth-providers.ee.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
import { Router } from 'express';
 | 
			
		||||
import { authenticateUser } from '../../../helpers/authentication.js';
 | 
			
		||||
import { authorizeAdmin } from '../../../helpers/authorization.js';
 | 
			
		||||
import { checkIsEnterprise } from '../../../helpers/check-is-enterprise.js';
 | 
			
		||||
import getSamlAuthProvidersAction from '../../../controllers/api/v1/admin/saml-auth-providers/get-saml-auth-providers.ee.js';
 | 
			
		||||
import getSamlAuthProviderAction from '../../../controllers/api/v1/admin/saml-auth-providers/get-saml-auth-provider.ee.js';
 | 
			
		||||
 | 
			
		||||
const router = Router();
 | 
			
		||||
 | 
			
		||||
router.get(
 | 
			
		||||
  '/',
 | 
			
		||||
  authenticateUser,
 | 
			
		||||
  authorizeAdmin,
 | 
			
		||||
  checkIsEnterprise,
 | 
			
		||||
  getSamlAuthProvidersAction
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
router.get(
 | 
			
		||||
  '/:samlAuthProviderId',
 | 
			
		||||
  authenticateUser,
 | 
			
		||||
  authorizeAdmin,
 | 
			
		||||
  checkIsEnterprise,
 | 
			
		||||
  getSamlAuthProviderAction
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export default router;
 | 
			
		||||
@@ -5,6 +5,7 @@ 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';
 | 
			
		||||
import samlAuthProvidersRouter from './api/v1/saml-auth-providers.ee.js';
 | 
			
		||||
 | 
			
		||||
const router = Router();
 | 
			
		||||
 | 
			
		||||
@@ -14,5 +15,6 @@ router.use('/paddle', paddleRouter);
 | 
			
		||||
router.use('/healthcheck', healthcheckRouter);
 | 
			
		||||
router.use('/api/v1/automatisch', automatischRouter);
 | 
			
		||||
router.use('/api/v1/users', usersRouter);
 | 
			
		||||
router.use('/api/v1/admin/saml-auth-providers', samlAuthProvidersRouter);
 | 
			
		||||
 | 
			
		||||
export default router;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,13 @@
 | 
			
		||||
import userSerializer from './user.js';
 | 
			
		||||
import roleSerializer from './role.js';
 | 
			
		||||
import permissionSerializer from './permission.js';
 | 
			
		||||
import samlAuthProviderSerializer from './saml-auth-provider.ee.js';
 | 
			
		||||
 | 
			
		||||
const serializers = {
 | 
			
		||||
  User: userSerializer,
 | 
			
		||||
  Role: roleSerializer,
 | 
			
		||||
  Permission: permissionSerializer,
 | 
			
		||||
  SamlAuthProvider: samlAuthProviderSerializer,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default serializers;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										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);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										18
									
								
								packages/backend/src/serializers/saml-auth-provider.ee.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								packages/backend/src/serializers/saml-auth-provider.ee.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
const samlAuthProviderSerializer = (samlAuthProvider) => {
 | 
			
		||||
  return {
 | 
			
		||||
    id: samlAuthProvider.id,
 | 
			
		||||
    name: samlAuthProvider.name,
 | 
			
		||||
    certificate: samlAuthProvider.certificate,
 | 
			
		||||
    signatureAlgorithm: samlAuthProvider.signatureAlgorithm,
 | 
			
		||||
    issuer: samlAuthProvider.issuer,
 | 
			
		||||
    entryPoint: samlAuthProvider.entryPoint,
 | 
			
		||||
    firstnameAttributeName: samlAuthProvider.firstnameAttributeName,
 | 
			
		||||
    surnameAttributeName: samlAuthProvider.surnameAttributeName,
 | 
			
		||||
    emailAttributeName: samlAuthProvider.emailAttributeName,
 | 
			
		||||
    roleAttributeName: samlAuthProvider.roleAttributeName,
 | 
			
		||||
    active: samlAuthProvider.active,
 | 
			
		||||
    defaultRoleId: samlAuthProvider.defaultRoleId,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default samlAuthProviderSerializer;
 | 
			
		||||
@@ -0,0 +1,32 @@
 | 
			
		||||
import { describe, it, expect, beforeEach } from 'vitest';
 | 
			
		||||
import { createSamlAuthProvider } from '../../test/factories/saml-auth-provider.ee.js';
 | 
			
		||||
import samlAuthProviderSerializer from './saml-auth-provider.ee.js';
 | 
			
		||||
 | 
			
		||||
describe('samlAuthProviderSerializer', () => {
 | 
			
		||||
  let samlAuthProvider;
 | 
			
		||||
 | 
			
		||||
  beforeEach(async () => {
 | 
			
		||||
    samlAuthProvider = await createSamlAuthProvider();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should return saml auth provider data', async () => {
 | 
			
		||||
    const expectedPayload = {
 | 
			
		||||
      id: samlAuthProvider.id,
 | 
			
		||||
      name: samlAuthProvider.name,
 | 
			
		||||
      certificate: samlAuthProvider.certificate,
 | 
			
		||||
      signatureAlgorithm: samlAuthProvider.signatureAlgorithm,
 | 
			
		||||
      issuer: samlAuthProvider.issuer,
 | 
			
		||||
      entryPoint: samlAuthProvider.entryPoint,
 | 
			
		||||
      firstnameAttributeName: samlAuthProvider.firstnameAttributeName,
 | 
			
		||||
      surnameAttributeName: samlAuthProvider.surnameAttributeName,
 | 
			
		||||
      emailAttributeName: samlAuthProvider.emailAttributeName,
 | 
			
		||||
      roleAttributeName: samlAuthProvider.roleAttributeName,
 | 
			
		||||
      active: samlAuthProvider.active,
 | 
			
		||||
      defaultRoleId: samlAuthProvider.defaultRoleId,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    expect(samlAuthProviderSerializer(samlAuthProvider)).toEqual(
 | 
			
		||||
      expectedPayload
 | 
			
		||||
    );
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										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);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										33
									
								
								packages/backend/test/factories/saml-auth-provider.ee.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								packages/backend/test/factories/saml-auth-provider.ee.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
import { createRole } from './role';
 | 
			
		||||
import SamlAuthProvider from '../../src/models/saml-auth-provider.ee.js';
 | 
			
		||||
 | 
			
		||||
export const createSamlAuthProvider = async (params = {}) => {
 | 
			
		||||
  params.name = params?.name || 'Keycloak SAML';
 | 
			
		||||
  params.certificate = params?.certificate || 'certificate';
 | 
			
		||||
  params.signatureAlgorithm = params?.signatureAlgorithm || 'sha512';
 | 
			
		||||
 | 
			
		||||
  params.entryPoint =
 | 
			
		||||
    params?.entryPoint ||
 | 
			
		||||
    'https://example.com/auth/realms/automatisch/protocol/saml';
 | 
			
		||||
 | 
			
		||||
  params.issuer = params?.issuer || 'automatisch-client';
 | 
			
		||||
 | 
			
		||||
  params.firstnameAttributeName =
 | 
			
		||||
    params?.firstnameAttributeName || 'urn:oid:2.1.1.42';
 | 
			
		||||
 | 
			
		||||
  params.surnameAttributeName =
 | 
			
		||||
    params?.surnameAttributeName || 'urn:oid:2.1.1.4';
 | 
			
		||||
 | 
			
		||||
  params.emailAttributeName =
 | 
			
		||||
    params?.emailAttributeName || 'urn:oid:1.1.2342.19200300.100.1.1';
 | 
			
		||||
 | 
			
		||||
  params.roleAttributeName = params?.roleAttributeName || 'Role';
 | 
			
		||||
  params.defaultRoleId = params?.defaultRoleId || (await createRole()).id;
 | 
			
		||||
  params.active = params?.active || true;
 | 
			
		||||
 | 
			
		||||
  const samlAuthProvider = await SamlAuthProvider.query()
 | 
			
		||||
    .insert(params)
 | 
			
		||||
    .returning('*');
 | 
			
		||||
 | 
			
		||||
  return samlAuthProvider;
 | 
			
		||||
};
 | 
			
		||||
@@ -0,0 +1,29 @@
 | 
			
		||||
const getSamlAuthProvidersMock = async (samlAuthProvider) => {
 | 
			
		||||
  const data = {
 | 
			
		||||
    active: samlAuthProvider.active,
 | 
			
		||||
    certificate: samlAuthProvider.certificate,
 | 
			
		||||
    defaultRoleId: samlAuthProvider.defaultRoleId,
 | 
			
		||||
    emailAttributeName: samlAuthProvider.emailAttributeName,
 | 
			
		||||
    entryPoint: samlAuthProvider.entryPoint,
 | 
			
		||||
    firstnameAttributeName: samlAuthProvider.firstnameAttributeName,
 | 
			
		||||
    id: samlAuthProvider.id,
 | 
			
		||||
    issuer: samlAuthProvider.issuer,
 | 
			
		||||
    name: samlAuthProvider.name,
 | 
			
		||||
    roleAttributeName: samlAuthProvider.roleAttributeName,
 | 
			
		||||
    signatureAlgorithm: samlAuthProvider.signatureAlgorithm,
 | 
			
		||||
    surnameAttributeName: samlAuthProvider.surnameAttributeName,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    data: data,
 | 
			
		||||
    meta: {
 | 
			
		||||
      count: 1,
 | 
			
		||||
      currentPage: null,
 | 
			
		||||
      isArray: false,
 | 
			
		||||
      totalPages: null,
 | 
			
		||||
      type: 'SamlAuthProvider',
 | 
			
		||||
    },
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default getSamlAuthProvidersMock;
 | 
			
		||||
@@ -0,0 +1,31 @@
 | 
			
		||||
const getSamlAuthProvidersMock = async (samlAuthProviders) => {
 | 
			
		||||
  const data = samlAuthProviders.map((samlAuthProvider) => {
 | 
			
		||||
    return {
 | 
			
		||||
      active: samlAuthProvider.active,
 | 
			
		||||
      certificate: samlAuthProvider.certificate,
 | 
			
		||||
      defaultRoleId: samlAuthProvider.defaultRoleId,
 | 
			
		||||
      emailAttributeName: samlAuthProvider.emailAttributeName,
 | 
			
		||||
      entryPoint: samlAuthProvider.entryPoint,
 | 
			
		||||
      firstnameAttributeName: samlAuthProvider.firstnameAttributeName,
 | 
			
		||||
      id: samlAuthProvider.id,
 | 
			
		||||
      issuer: samlAuthProvider.issuer,
 | 
			
		||||
      name: samlAuthProvider.name,
 | 
			
		||||
      roleAttributeName: samlAuthProvider.roleAttributeName,
 | 
			
		||||
      signatureAlgorithm: samlAuthProvider.signatureAlgorithm,
 | 
			
		||||
      surnameAttributeName: samlAuthProvider.surnameAttributeName,
 | 
			
		||||
    };
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    data: data,
 | 
			
		||||
    meta: {
 | 
			
		||||
      count: data.length,
 | 
			
		||||
      currentPage: null,
 | 
			
		||||
      isArray: true,
 | 
			
		||||
      totalPages: null,
 | 
			
		||||
      type: 'SamlAuthProvider',
 | 
			
		||||
    },
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default getSamlAuthProvidersMock;
 | 
			
		||||
@@ -252,6 +252,16 @@ export default defineConfig({
 | 
			
		||||
            { text: 'Connection', link: '/apps/openai/connection' },
 | 
			
		||||
          ],
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          text: 'PDFMonkey',
 | 
			
		||||
          collapsible: true,
 | 
			
		||||
          collapsed: true,
 | 
			
		||||
          items: [
 | 
			
		||||
            { text: 'Triggers', link: '/apps/pdf-monkey/triggers' },
 | 
			
		||||
            { text: 'Actions', link: '/apps/pdf-monkey/actions' },
 | 
			
		||||
            { text: 'Connection', link: '/apps/pdf-monkey/connection' },
 | 
			
		||||
          ],
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          text: 'Pipedrive',
 | 
			
		||||
          collapsible: true,
 | 
			
		||||
@@ -305,7 +315,7 @@ export default defineConfig({
 | 
			
		||||
          collapsed: true,
 | 
			
		||||
          items: [
 | 
			
		||||
            { text: 'Actions', link: '/apps/removebg/actions' },
 | 
			
		||||
            { text: 'Connection', link: '/apps/removebg/connection' }
 | 
			
		||||
            { text: 'Connection', link: '/apps/removebg/connection' },
 | 
			
		||||
          ],
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								packages/docs/pages/apps/pdf-monkey/actions.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								packages/docs/pages/apps/pdf-monkey/actions.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
---
 | 
			
		||||
favicon: /favicons/pdf-monkey.svg
 | 
			
		||||
items:
 | 
			
		||||
  - name: Generate documents
 | 
			
		||||
    desc: Creates a new document.
 | 
			
		||||
  - name: Find documents
 | 
			
		||||
    desc: Finds a document.
 | 
			
		||||
  - name: Delete documents
 | 
			
		||||
    desc: Deletes a document.
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
<script setup>
 | 
			
		||||
  import CustomListing from '../../components/CustomListing.vue'
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<CustomListing />
 | 
			
		||||
							
								
								
									
										11
									
								
								packages/docs/pages/apps/pdf-monkey/connection.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								packages/docs/pages/apps/pdf-monkey/connection.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
# PDFMonkey
 | 
			
		||||
 | 
			
		||||
:::info
 | 
			
		||||
This page explains the steps you need to follow to set up the PDFMonkey
 | 
			
		||||
connection in Automatisch. If any of the steps are outdated, please let us know!
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
1. Login to your PDFMonkey account: [https://dashboard.pdfmonkey.io/login](https://dashboard.pdfmonkey.io/login).
 | 
			
		||||
2. Go to **My Account** section from your profile.
 | 
			
		||||
3. Copy `API SECRET KEY` from the page to the `API Key` field on Automatisch.
 | 
			
		||||
4. Now, you can start using the PDFMonkey connection with Automatisch.
 | 
			
		||||
							
								
								
									
										12
									
								
								packages/docs/pages/apps/pdf-monkey/triggers.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								packages/docs/pages/apps/pdf-monkey/triggers.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
---
 | 
			
		||||
favicon: /favicons/pdf-monkey.svg
 | 
			
		||||
items:
 | 
			
		||||
  - name: Documents Generated
 | 
			
		||||
    desc: Triggers upon the successful completion of document generation.
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
<script setup>
 | 
			
		||||
  import CustomListing from '../../components/CustomListing.vue'
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<CustomListing />
 | 
			
		||||
@@ -26,6 +26,7 @@ The following integrations are currently supported by Automatisch.
 | 
			
		||||
- [Ntfy](/apps/ntfy/actions)
 | 
			
		||||
- [Odoo](/apps/odoo/actions)
 | 
			
		||||
- [OpenAI](/apps/openai/actions)
 | 
			
		||||
- [PDFMonkey](/apps/pdf-monkey/actions)
 | 
			
		||||
- [Pipedrive](/apps/pipedrive/triggers)
 | 
			
		||||
- [Placetel](/apps/placetel/triggers)
 | 
			
		||||
- [PostgreSQL](/apps/postgresql/actions)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1487
									
								
								packages/docs/pages/public/favicons/pdf-monkey.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1487
									
								
								packages/docs/pages/public/favicons/pdf-monkey.svg
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
		 After Width: | Height: | Size: 112 KiB  | 
@@ -3,6 +3,7 @@ import Box from '@mui/material/Box';
 | 
			
		||||
import Toolbar from '@mui/material/Toolbar';
 | 
			
		||||
import { useTheme } from '@mui/material/styles';
 | 
			
		||||
import useMediaQuery from '@mui/material/useMediaQuery';
 | 
			
		||||
import Stack from '@mui/material/Stack';
 | 
			
		||||
import AppsIcon from '@mui/icons-material/Apps';
 | 
			
		||||
import SwapCallsIcon from '@mui/icons-material/SwapCalls';
 | 
			
		||||
import HistoryIcon from '@mui/icons-material/History';
 | 
			
		||||
@@ -139,7 +140,7 @@ export default function PublicLayout({
 | 
			
		||||
        onDrawerClose={closeDrawer}
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
      <Box sx={{ display: 'flex' }}>
 | 
			
		||||
      <Box sx={{ display: 'flex', height: '100%' }}>
 | 
			
		||||
        <Drawer
 | 
			
		||||
          links={drawerLinks}
 | 
			
		||||
          bottomLinks={bottomLinks}
 | 
			
		||||
@@ -148,11 +149,10 @@ export default function PublicLayout({
 | 
			
		||||
          onClose={closeDrawer}
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
        <Box sx={{ flex: 1 }}>
 | 
			
		||||
        <Stack flex={1}>
 | 
			
		||||
          <Toolbar />
 | 
			
		||||
 | 
			
		||||
          {children}
 | 
			
		||||
        </Box>
 | 
			
		||||
        </Stack>
 | 
			
		||||
      </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.inputName": "Name",
 | 
			
		||||
  "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 NoResultFound from 'components/NotFound';
 | 
			
		||||
import PublicLayout from 'components/PublicLayout';
 | 
			
		||||
import Applications from 'pages/Applications';
 | 
			
		||||
import Application from 'pages/Application';
 | 
			
		||||
@@ -17,9 +18,15 @@ import * as URLS from 'config/urls';
 | 
			
		||||
import settingsRoutes from './settingsRoutes';
 | 
			
		||||
import adminSettingsRoutes from './adminSettingsRoutes';
 | 
			
		||||
import Notifications from 'pages/Notifications';
 | 
			
		||||
import useConfig from 'hooks/useConfig';
 | 
			
		||||
import useAuthentication from 'hooks/useAuthentication';
 | 
			
		||||
 | 
			
		||||
export default (
 | 
			
		||||
  <Routes>
 | 
			
		||||
function Routes() {
 | 
			
		||||
  const { config } = useConfig();
 | 
			
		||||
  const { isAuthenticated } = useAuthentication();
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <ReactRouterRoutes>
 | 
			
		||||
      <Route
 | 
			
		||||
        path={URLS.EXECUTIONS}
 | 
			
		||||
        element={
 | 
			
		||||
@@ -85,10 +92,7 @@ export default (
 | 
			
		||||
        }
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
    <Route
 | 
			
		||||
      path={URLS.LOGIN_CALLBACK}
 | 
			
		||||
      element={<LoginCallback />}
 | 
			
		||||
    />
 | 
			
		||||
      <Route path={URLS.LOGIN_CALLBACK} element={<LoginCallback />} />
 | 
			
		||||
 | 
			
		||||
      <Route
 | 
			
		||||
        path={URLS.SIGNUP}
 | 
			
		||||
@@ -117,6 +121,7 @@ export default (
 | 
			
		||||
        }
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
      {!config?.disableNotificationsPage && (
 | 
			
		||||
        <Route
 | 
			
		||||
          path={URLS.UPDATES}
 | 
			
		||||
          element={
 | 
			
		||||
@@ -125,19 +130,22 @@ export default (
 | 
			
		||||
            </Layout>
 | 
			
		||||
          }
 | 
			
		||||
        />
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
    <Route path="/" element={<Navigate to={URLS.FLOWS} replace />} />
 | 
			
		||||
      <Route
 | 
			
		||||
        path="/"
 | 
			
		||||
        element={
 | 
			
		||||
          <Navigate to={isAuthenticated ? URLS.FLOWS : URLS.LOGIN} replace />
 | 
			
		||||
        }
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
      <Route path={URLS.SETTINGS}>{settingsRoutes}</Route>
 | 
			
		||||
 | 
			
		||||
      <Route path={URLS.ADMIN_SETTINGS}>{adminSettingsRoutes}</Route>
 | 
			
		||||
 | 
			
		||||
    <Route
 | 
			
		||||
      element={
 | 
			
		||||
        <Layout>
 | 
			
		||||
          <div>404</div>
 | 
			
		||||
        </Layout>
 | 
			
		||||
      }
 | 
			
		||||
    />
 | 
			
		||||
  </Routes>
 | 
			
		||||
);
 | 
			
		||||
      <Route path="*" element={<NoResultFound />} />
 | 
			
		||||
    </ReactRouterRoutes>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default <Routes />;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user