mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 04:34:06 -08:00
🔀 Merge branch 'clockify' of https://github.com/dominion-solutions/n8n into dominion-solutions-clockify
This commit is contained in:
commit
842fda9462
21
packages/nodes-base/credentials/ClockifyApi.credentials.ts
Normal file
21
packages/nodes-base/credentials/ClockifyApi.credentials.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import {
|
||||
ICredentialType,
|
||||
NodePropertyTypes,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
|
||||
export class ClockifyApi implements ICredentialType {
|
||||
name = 'clockifyApi';
|
||||
displayName = 'Clockify API';
|
||||
properties = [
|
||||
// The credentials to get from user and save encrypted.
|
||||
// Properties can be defined exactly in the same way
|
||||
// as node properties.
|
||||
{
|
||||
displayName: 'API Key',
|
||||
name: 'apiKey',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
},
|
||||
];
|
||||
}
|
121
packages/nodes-base/nodes/Clockify/ClockifyTrigger.node.ts
Normal file
121
packages/nodes-base/nodes/Clockify/ClockifyTrigger.node.ts
Normal file
|
@ -0,0 +1,121 @@
|
|||
import {IPollFunctions} from 'n8n-core';
|
||||
import {
|
||||
IDataObject,
|
||||
ILoadOptionsFunctions,
|
||||
INodeExecutionData,
|
||||
INodePropertyOptions,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
clockifyApiRequest,
|
||||
} from './GenericFunctions';
|
||||
|
||||
import {IWorkspaceDto} from "./WorkpaceInterfaces";
|
||||
import {EntryTypeEnum} from "./EntryTypeEnum";
|
||||
import {ICurrentUserDto} from "./UserDtos";
|
||||
import * as moment from "moment";
|
||||
|
||||
|
||||
export class ClockifyTrigger implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Clockify Event',
|
||||
icon: 'file:images/clockify-mark-blue.png',
|
||||
name: 'clockifyTrigger',
|
||||
group: ['trigger'],
|
||||
version: 1,
|
||||
description: 'Watches Clockify For Events',
|
||||
defaults: {
|
||||
name: 'Clockify Event',
|
||||
color: '#00FF00',
|
||||
},
|
||||
inputs: [],
|
||||
outputs: ['main'],
|
||||
credentials: [
|
||||
{
|
||||
name: 'clockifyApi',
|
||||
required: true,
|
||||
}
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Workspace',
|
||||
name: 'workspaceId',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'listWorkspaces',
|
||||
},
|
||||
required: true,
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Trigger',
|
||||
name: 'watchField',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'New Time Entry',
|
||||
value: EntryTypeEnum.NEW_TIME_ENTRY,
|
||||
}
|
||||
],
|
||||
required: true,
|
||||
default: '',
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
methods = {
|
||||
loadOptions: {
|
||||
async listWorkspaces(this: ILoadOptionsFunctions) : Promise<INodePropertyOptions[]> {
|
||||
const rtv : INodePropertyOptions[] = [];
|
||||
const workspaces: IWorkspaceDto[] = await clockifyApiRequest.call(this,'GET', 'workspaces');
|
||||
if(undefined !== workspaces) {
|
||||
workspaces.forEach(value => {
|
||||
rtv.push(
|
||||
{
|
||||
name: value.name,
|
||||
value: value.id,
|
||||
});
|
||||
});
|
||||
}
|
||||
return rtv;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async poll(this: IPollFunctions): Promise<INodeExecutionData[][] | null> {
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
const triggerField = this.getNodeParameter('watchField') as EntryTypeEnum;
|
||||
const workspaceId = this.getNodeParameter('workspaceId');
|
||||
|
||||
const userInfo : ICurrentUserDto = await clockifyApiRequest.call(this,'GET', 'user');
|
||||
const qs : IDataObject = {};
|
||||
let resource: string;
|
||||
let result = null;
|
||||
|
||||
switch (triggerField) {
|
||||
case EntryTypeEnum.NEW_TIME_ENTRY :
|
||||
default:
|
||||
resource = `workspaces/${workspaceId}/user/${userInfo.id}/time-entries`;
|
||||
qs.start = webhookData.lastTimeChecked;
|
||||
qs.end = moment().toISOString();
|
||||
qs.hydrated = true;
|
||||
qs['in-progress'] = false;
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
result = await clockifyApiRequest.call(this, 'GET', resource, {}, qs );
|
||||
webhookData.lastTimeChecked = qs.end_date;
|
||||
}
|
||||
catch( e ) {
|
||||
throw new Error(`Clockify Exception: ${e}`);
|
||||
}
|
||||
if (Array.isArray(result) && result.length !== 0) {
|
||||
result = [this.helpers.returnJsonArray(result)];
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
}
|
19
packages/nodes-base/nodes/Clockify/CommonDtos.ts
Normal file
19
packages/nodes-base/nodes/Clockify/CommonDtos.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
export interface IHourlyRateDto {
|
||||
amount: number;
|
||||
currency: string;
|
||||
}
|
||||
|
||||
enum MembershipStatusEnum {
|
||||
PENDING = "PENDING",
|
||||
ACTIVE = "ACTIVE",
|
||||
DECLINED = "DECLINED",
|
||||
INACTIVE = "INACTIVE"
|
||||
}
|
||||
|
||||
export interface IMembershipDto {
|
||||
hourlyRate: IHourlyRateDto;
|
||||
membershipStatus: MembershipStatusEnum;
|
||||
membershipType: string;
|
||||
targetId: string;
|
||||
userId: string;
|
||||
}
|
3
packages/nodes-base/nodes/Clockify/EntryTypeEnum.ts
Normal file
3
packages/nodes-base/nodes/Clockify/EntryTypeEnum.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export enum EntryTypeEnum {
|
||||
NEW_TIME_ENTRY,
|
||||
}
|
38
packages/nodes-base/nodes/Clockify/GenericFunctions.ts
Normal file
38
packages/nodes-base/nodes/Clockify/GenericFunctions.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
import { OptionsWithUri } from 'request';
|
||||
import {
|
||||
ILoadOptionsFunctions,
|
||||
IPollFunctions
|
||||
} from "n8n-core";
|
||||
|
||||
import {IDataObject} from "n8n-workflow";
|
||||
|
||||
export async function clockifyApiRequest(this: ILoadOptionsFunctions | IPollFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||
const credentials = this.getCredentials('clockifyApi');
|
||||
if (credentials === undefined) {
|
||||
throw new Error('No credentials got returned!');
|
||||
}
|
||||
const BASE_URL = `https://api.clockify.me/api/v1`;
|
||||
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Api-Key': credentials.apiKey as string,
|
||||
},
|
||||
method,
|
||||
qs,
|
||||
body,
|
||||
uri: `${BASE_URL}/${resource}`,
|
||||
json: true
|
||||
};
|
||||
try {
|
||||
return await this.helpers.request!(options);
|
||||
} catch (error) {
|
||||
|
||||
let errorMessage = error.message;
|
||||
if (error.response.body && error.response.body.message) {
|
||||
errorMessage = `[${error.response.body.status_code}] ${error.response.body.message}`;
|
||||
}
|
||||
|
||||
throw new Error('Clockify Error: ' + errorMessage);
|
||||
}
|
||||
}
|
20
packages/nodes-base/nodes/Clockify/UserDtos.ts
Normal file
20
packages/nodes-base/nodes/Clockify/UserDtos.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import {IMembershipDto} from "./CommonDtos";
|
||||
|
||||
enum UserStatusEnum {
|
||||
ACTIVE, PENDING_EMAIL_VERIFICATION, DELETED
|
||||
}
|
||||
|
||||
interface IUserSettingsDto {
|
||||
}
|
||||
|
||||
export interface ICurrentUserDto {
|
||||
activeWorkspace: string;
|
||||
defaultWorkspace: string;
|
||||
email: string;
|
||||
id: string;
|
||||
memberships: IMembershipDto [];
|
||||
name: string;
|
||||
profilePicture: string;
|
||||
settings: IUserSettingsDto;
|
||||
status: UserStatusEnum;
|
||||
}
|
77
packages/nodes-base/nodes/Clockify/WorkpaceInterfaces.ts
Normal file
77
packages/nodes-base/nodes/Clockify/WorkpaceInterfaces.ts
Normal file
|
@ -0,0 +1,77 @@
|
|||
import {IHourlyRateDto, IMembershipDto} from "./CommonDtos";
|
||||
|
||||
enum AdminOnlyPagesEnum {
|
||||
PROJECT ="PROJECT",
|
||||
TEAM = "TEAM",
|
||||
REPORTS = "REPORTS"
|
||||
}
|
||||
|
||||
enum DaysOfWeekEnum {
|
||||
MONDAY = "MONDAY",
|
||||
TUESDAY = "TUESDAY",
|
||||
WEDNESDAY = "WEDNESDAY",
|
||||
THURSDAY = "THURSDAY",
|
||||
FRIDAY = "FRIDAY",
|
||||
SATURDAY = "SATURDAY",
|
||||
SUNDAY = "SUNDAY"
|
||||
}
|
||||
|
||||
enum DatePeriodEnum {
|
||||
DAYS="DAYS",
|
||||
WEEKS = "WEEKS",
|
||||
MONTHS = "MONTHS"
|
||||
}
|
||||
|
||||
enum AutomaticLockTypeEnum {
|
||||
WEEKLY = "WEEKLY",
|
||||
MONTHLY = "MONTHLY",
|
||||
OLDER_THAN = "OLDER_THAN"
|
||||
}
|
||||
|
||||
interface IAutomaticLockDto {
|
||||
changeDay: DaysOfWeekEnum;
|
||||
dayOfMonth: number;
|
||||
firstDay: DaysOfWeekEnum;
|
||||
olderThanPeriod: DatePeriodEnum;
|
||||
olderThanValue: number;
|
||||
type: AutomaticLockTypeEnum;
|
||||
}
|
||||
|
||||
interface IRound {
|
||||
minutes: string;
|
||||
round: string;
|
||||
}
|
||||
|
||||
interface IWorkspaceSettingsDto {
|
||||
adminOnlyPages: AdminOnlyPagesEnum[];
|
||||
automaticLock: IAutomaticLockDto;
|
||||
canSeeTimeSheet: boolean;
|
||||
defaultBillableProjects: boolean;
|
||||
forceDescription: boolean;
|
||||
forceProjects: boolean;
|
||||
forceTags: boolean;
|
||||
forceTasks: boolean;
|
||||
lockTimeEntries: string;
|
||||
onlyAdminsCreateProject: boolean;
|
||||
onlyAdminsCreateTag: boolean;
|
||||
onlyAdminsSeeAllTimeEntries: boolean;
|
||||
onlyAdminsSeeBillableRates: boolean;
|
||||
onlyAdminsSeeDashboard: boolean;
|
||||
onlyAdminsSeePublicProjectsEntries: boolean;
|
||||
projectFavorites: boolean;
|
||||
projectGroupingLabel: string;
|
||||
projectPickerSpecialFilter: boolean;
|
||||
round: IRound;
|
||||
timeRoundingInReports: boolean;
|
||||
trackTimeDownToSecond: boolean;
|
||||
}
|
||||
|
||||
export interface IWorkspaceDto {
|
||||
hourlyRate: IHourlyRateDto;
|
||||
id: string;
|
||||
imageUrl: string;
|
||||
memberships: IMembershipDto[];
|
||||
name: string;
|
||||
workspaceSettings: IWorkspaceSettingsDto;
|
||||
}
|
||||
|
BIN
packages/nodes-base/nodes/Clockify/images/clockify-mark-blue.png
Normal file
BIN
packages/nodes-base/nodes/Clockify/images/clockify-mark-blue.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.8 KiB |
|
@ -37,6 +37,7 @@
|
|||
"dist/credentials/ChargebeeApi.credentials.js",
|
||||
"dist/credentials/ClearbitApi.credentials.js",
|
||||
"dist/credentials/ClickUpApi.credentials.js",
|
||||
"dist/credentials/ClockifyApi.credentials.js",
|
||||
"dist/credentials/CodaApi.credentials.js",
|
||||
"dist/credentials/CopperApi.credentials.js",
|
||||
"dist/credentials/DisqusApi.credentials.js",
|
||||
|
@ -118,6 +119,7 @@
|
|||
"dist/nodes/Clearbit/Clearbit.node.js",
|
||||
"dist/nodes/ClickUp/ClickUp.node.js",
|
||||
"dist/nodes/ClickUp/ClickUpTrigger.node.js",
|
||||
"dist/nodes/Clockify/ClockifyTrigger.node.js",
|
||||
"dist/nodes/Coda/Coda.node.js",
|
||||
"dist/nodes/Copper/CopperTrigger.node.js",
|
||||
"dist/nodes/Cron.node.js",
|
||||
|
@ -293,3 +295,4 @@
|
|||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue