Add Activepieces integration for workflow automation
- Add Activepieces fork with SmoothSchedule custom piece - Create integrations app with Activepieces service layer - Add embed token endpoint for iframe integration - Create Automations page with embedded workflow builder - Add sidebar visibility fix for embed mode - Add list inactive customers endpoint to Public API - Include SmoothSchedule triggers: event created/updated/cancelled - Include SmoothSchedule actions: create/update/cancel events, list resources/services/customers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { gitlabAuth } from '../../';
|
||||
import { gitlabCommon, makeClient } from '../common';
|
||||
|
||||
export const createIssueAction = createAction({
|
||||
auth: gitlabAuth,
|
||||
name: 'create_issue',
|
||||
description: 'Create a project issue',
|
||||
displayName: 'Create Issue',
|
||||
props: {
|
||||
projectId: gitlabCommon.projectId(),
|
||||
title: Property.ShortText({
|
||||
displayName: 'Issue Title',
|
||||
required: true,
|
||||
}),
|
||||
description: Property.LongText({
|
||||
displayName: 'Issue Description',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
async run({ propsValue, auth }) {
|
||||
const { projectId, title, description } = propsValue;
|
||||
const client = makeClient({ auth });
|
||||
return await client.createProjectIssue(projectId as string, {
|
||||
title: title,
|
||||
description: description,
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,96 @@
|
||||
import {
|
||||
AuthenticationType,
|
||||
HttpMessageBody,
|
||||
HttpMethod,
|
||||
QueryParams,
|
||||
httpClient,
|
||||
} from '@activepieces/pieces-common';
|
||||
import {
|
||||
ListProjectsRequest,
|
||||
CreateProjectIssueRequest,
|
||||
GitlabProject,
|
||||
ProjectWebhookRequest,
|
||||
ProjectWebhook,
|
||||
} from './models';
|
||||
export class GitlabApi {
|
||||
constructor(private accessToken: string) {}
|
||||
|
||||
async makeRequest<T extends HttpMessageBody>(
|
||||
method: HttpMethod,
|
||||
url: string,
|
||||
query?: QueryParams,
|
||||
body?: object
|
||||
): Promise<T> {
|
||||
const res = await httpClient.sendRequest<T>({
|
||||
method,
|
||||
url: 'https://gitlab.com/api/v4' + url,
|
||||
queryParams: query,
|
||||
body,
|
||||
authentication: {
|
||||
type: AuthenticationType.BEARER_TOKEN,
|
||||
token: this.accessToken,
|
||||
},
|
||||
});
|
||||
return res.body;
|
||||
}
|
||||
async listProjects(request: ListProjectsRequest) {
|
||||
return this.makeRequest<GitlabProject[]>(
|
||||
HttpMethod.GET,
|
||||
'/projects',
|
||||
prepareQuery(request)
|
||||
);
|
||||
}
|
||||
async createProjectIssue(
|
||||
projectId: string,
|
||||
request: CreateProjectIssueRequest
|
||||
) {
|
||||
return this.makeRequest(
|
||||
HttpMethod.POST,
|
||||
`/projects/${projectId}/issues`,
|
||||
undefined,
|
||||
request
|
||||
);
|
||||
}
|
||||
async subscribeProjectWebhook(
|
||||
projectId: string,
|
||||
request: ProjectWebhookRequest
|
||||
) {
|
||||
return this.makeRequest<ProjectWebhook>(
|
||||
HttpMethod.POST,
|
||||
`/projects/${projectId}/hooks`,
|
||||
undefined,
|
||||
request
|
||||
);
|
||||
}
|
||||
async unsubscribeProjectWebhook(projectId: string, webhookId: string) {
|
||||
return this.makeRequest(
|
||||
HttpMethod.DELETE,
|
||||
`/projects/${projectId}/hooks/${webhookId}`,
|
||||
undefined
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function emptyValueFilter(
|
||||
accessor: (key: string) => any
|
||||
): (key: string) => boolean {
|
||||
return (key: string) => {
|
||||
const val = accessor(key);
|
||||
return (
|
||||
val !== null &&
|
||||
val !== undefined &&
|
||||
(typeof val != 'string' || val.length > 0)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export function prepareQuery(request?: Record<string, any>): QueryParams {
|
||||
const params: QueryParams = {};
|
||||
if (!request) return params;
|
||||
Object.keys(request)
|
||||
.filter(emptyValueFilter((k) => request[k]))
|
||||
.forEach((k: string) => {
|
||||
params[k] = (request as Record<string, any>)[k].toString();
|
||||
});
|
||||
return params;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { getAccessTokenOrThrow } from '@activepieces/pieces-common';
|
||||
import { OAuth2PropertyValue, Property } from '@activepieces/pieces-framework';
|
||||
import { GitlabApi } from './client';
|
||||
import { gitlabAuth } from '../..';
|
||||
|
||||
export const gitlabCommon = {
|
||||
projectId: (required = true) =>
|
||||
Property.Dropdown({
|
||||
auth: gitlabAuth,
|
||||
displayName: 'Project',
|
||||
required,
|
||||
refreshers: [],
|
||||
options: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
placeholder: 'Setup authentication first',
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
const client = makeClient({
|
||||
auth: auth,
|
||||
});
|
||||
const res = await client.listProjects({
|
||||
simple: true,
|
||||
membership: true,
|
||||
});
|
||||
return {
|
||||
disabled: false,
|
||||
options: res.map((project) => {
|
||||
return {
|
||||
label: project.name,
|
||||
value: project.id,
|
||||
};
|
||||
}),
|
||||
};
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
export function makeClient(propsValue: {
|
||||
auth: OAuth2PropertyValue;
|
||||
}): GitlabApi {
|
||||
const token = getAccessTokenOrThrow(propsValue.auth);
|
||||
return new GitlabApi(token);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
export interface ListProjectsRequest {
|
||||
membership?: boolean;
|
||||
simple?: boolean;
|
||||
}
|
||||
export interface ProjectWebhookRequest {
|
||||
url: string;
|
||||
confidential_issues_events?: boolean;
|
||||
confidential_note_events?: boolean;
|
||||
deployment_events?: boolean;
|
||||
enable_ssl_verification?: boolean;
|
||||
issues_events?: boolean;
|
||||
job_events?: boolean;
|
||||
merge_requests_events?: boolean;
|
||||
note_events?: boolean;
|
||||
pipeline_events?: boolean;
|
||||
push_events_branch_filter?: string;
|
||||
push_events?: boolean;
|
||||
releases_events?: boolean;
|
||||
tag_push_events?: boolean;
|
||||
wiki_page_events?: boolean;
|
||||
token?: boolean;
|
||||
}
|
||||
export interface CreateProjectIssueRequest {
|
||||
title: string;
|
||||
description?: string;
|
||||
}
|
||||
export interface ProjectWebhook {
|
||||
id: string;
|
||||
}
|
||||
export interface GitlabProject {
|
||||
id: string;
|
||||
description?: string;
|
||||
name: string;
|
||||
name_with_namespace: string;
|
||||
path: string;
|
||||
path_with_namespace: string;
|
||||
created_at: string;
|
||||
default_branch: string;
|
||||
tag_list?: string[];
|
||||
topics?: string[];
|
||||
ssh_url_to_repo: string;
|
||||
http_url_to_repo: string;
|
||||
web_url: string;
|
||||
avatar_url?: string;
|
||||
star_count: number;
|
||||
last_activity_at: string;
|
||||
namespace: {
|
||||
id: number;
|
||||
name: string;
|
||||
path: string;
|
||||
kind: string;
|
||||
full_path: string;
|
||||
parent_id?: string;
|
||||
avatar_url?: string;
|
||||
web_url: string;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
import {
|
||||
createTrigger,
|
||||
TriggerStrategy,
|
||||
Property,
|
||||
} from '@activepieces/pieces-framework';
|
||||
|
||||
import { gitlabCommon, makeClient } from '../common';
|
||||
import { gitlabAuth } from '../..';
|
||||
const sampleData = {
|
||||
object_kind: 'issue',
|
||||
event_type: 'issue',
|
||||
user: {
|
||||
id: 15269532,
|
||||
name: 'Kishan Parmar',
|
||||
username: 'kishanprmr',
|
||||
avatar_url:
|
||||
'https://secure.gravatar.com/avatar/4eabe7154116891e3ebc205e9754a832?s=80&d=identicon',
|
||||
email: '[REDACTED]',
|
||||
},
|
||||
project: {
|
||||
id: 48074457,
|
||||
name: 'basic-project-demo',
|
||||
description: null,
|
||||
web_url: 'https://gitlab.com/basic-group-demo/basic-project-demo',
|
||||
avatar_url: null,
|
||||
git_ssh_url: 'git@gitlab.com:basic-group-demo/basic-project-demo.git',
|
||||
git_http_url: 'https://gitlab.com/basic-group-demo/basic-project-demo.git',
|
||||
namespace: 'basic-group-demo',
|
||||
visibility_level: 0,
|
||||
path_with_namespace: 'basic-group-demo/basic-project-demo',
|
||||
default_branch: 'main',
|
||||
ci_config_path: '',
|
||||
homepage: 'https://gitlab.com/basic-group-demo/basic-project-demo',
|
||||
url: 'git@gitlab.com:basic-group-demo/basic-project-demo.git',
|
||||
ssh_url: 'git@gitlab.com:basic-group-demo/basic-project-demo.git',
|
||||
http_url: 'https://gitlab.com/basic-group-demo/basic-project-demo.git',
|
||||
},
|
||||
object_attributes: {
|
||||
author_id: 15269532,
|
||||
closed_at: null,
|
||||
confidential: false,
|
||||
created_at: '2023-09-01 07:03:02 UTC',
|
||||
description: '',
|
||||
discussion_locked: null,
|
||||
due_date: null,
|
||||
id: 133093523,
|
||||
iid: 32,
|
||||
last_edited_at: null,
|
||||
last_edited_by_id: null,
|
||||
milestone_id: null,
|
||||
moved_to_id: null,
|
||||
duplicated_to_id: null,
|
||||
project_id: 48074457,
|
||||
relative_position: null,
|
||||
state_id: 1,
|
||||
time_estimate: 0,
|
||||
title: 'Activepieces Testing',
|
||||
updated_at: '2023-09-01 07:03:02 UTC',
|
||||
updated_by_id: null,
|
||||
weight: null,
|
||||
health_status: null,
|
||||
url: 'https://gitlab.com/basic-group-demo/basic-project-demo/-/issues/32',
|
||||
total_time_spent: 0,
|
||||
time_change: 0,
|
||||
human_total_time_spent: null,
|
||||
human_time_change: null,
|
||||
human_time_estimate: null,
|
||||
assignee_ids: [],
|
||||
assignee_id: null,
|
||||
labels: [],
|
||||
state: 'opened',
|
||||
severity: 'unknown',
|
||||
customer_relations_contacts: [],
|
||||
action: 'open',
|
||||
},
|
||||
labels: [],
|
||||
changes: {
|
||||
author_id: { previous: null, current: 15269532 },
|
||||
created_at: { previous: null, current: '2023-09-01 07:03:02 UTC' },
|
||||
description: { previous: null, current: '' },
|
||||
id: { previous: null, current: 133093523 },
|
||||
iid: { previous: null, current: 32 },
|
||||
project_id: { previous: null, current: 48074457 },
|
||||
time_estimate: { previous: null, current: 0 },
|
||||
title: { previous: null, current: 'Activepieces Testing' },
|
||||
updated_at: { previous: null, current: '2023-09-01 07:03:02 UTC' },
|
||||
},
|
||||
repository: {
|
||||
name: 'basic-project-demo',
|
||||
url: 'git@gitlab.com:basic-group-demo/basic-project-demo.git',
|
||||
description: null,
|
||||
homepage: 'https://gitlab.com/basic-group-demo/basic-project-demo',
|
||||
},
|
||||
};
|
||||
|
||||
export const issuesEventTrigger = createTrigger({
|
||||
auth: gitlabAuth,
|
||||
name: 'project_issue_event',
|
||||
displayName: 'New Project Issue Event',
|
||||
description:
|
||||
'Triggers on project issue events when an issue is created or when an existing issue is updated, closed, or reopened.',
|
||||
props: {
|
||||
projectId: gitlabCommon.projectId(),
|
||||
actiontype: Property.StaticDropdown({
|
||||
displayName: 'Issue Event',
|
||||
description: 'Issue Event type for trigger',
|
||||
defaultValue: 'all',
|
||||
required: true,
|
||||
options: {
|
||||
disabled: false,
|
||||
options: [
|
||||
{ label: 'All', value: 'all' },
|
||||
{ label: 'Opened', value: 'open' },
|
||||
{ label: 'Closed', value: 'close' },
|
||||
{ label: 'Updated', value: 'update' },
|
||||
],
|
||||
},
|
||||
}),
|
||||
},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
sampleData: sampleData,
|
||||
|
||||
async onEnable({ store, auth, propsValue, webhookUrl }) {
|
||||
const projectId = propsValue.projectId!;
|
||||
const client = makeClient({ auth });
|
||||
const res = await client.subscribeProjectWebhook(projectId, {
|
||||
url: webhookUrl,
|
||||
issues_events: true,
|
||||
push_events: false,
|
||||
});
|
||||
await store.put<WebhookInformation>('gitlab_issue_trigger', {
|
||||
webhookId: res.id,
|
||||
projectId: projectId as string,
|
||||
});
|
||||
},
|
||||
|
||||
async onDisable({ auth, store }) {
|
||||
const response = await store.get<WebhookInformation>(
|
||||
'gitlab_issue_trigger'
|
||||
);
|
||||
if (response !== null && response !== undefined) {
|
||||
const client = makeClient({ auth });
|
||||
client.unsubscribeProjectWebhook(response.projectId, response.webhookId);
|
||||
}
|
||||
},
|
||||
|
||||
async run(context) {
|
||||
const { actiontype } = context.propsValue;
|
||||
if (
|
||||
isVerificationCall(
|
||||
context.payload.body as Record<string, unknown>,
|
||||
actiontype as string
|
||||
)
|
||||
) {
|
||||
return [context.payload.body];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
});
|
||||
|
||||
function isVerificationCall(payload: Record<string, any>, actiontype: string) {
|
||||
if (actiontype == 'all') {
|
||||
return true;
|
||||
}
|
||||
return payload['object_attributes']['action'] == actiontype;
|
||||
}
|
||||
|
||||
interface WebhookInformation {
|
||||
webhookId: string;
|
||||
projectId: string;
|
||||
}
|
||||
Reference in New Issue
Block a user