docs/add-new-integration.md
This guide explains how to add a new issue tracker integration to Super Productivity.
Super Productivity supports multiple issue tracker integrations (called "Issue Providers" in the codebase), including Jira, GitLab, Gitea, Redmine, Open Project, CalDAV, Calendar (iCal), Trello, ClickUp, Linear, Azure DevOps, and Nextcloud Deck. GitHub has been migrated to a plugin-based provider. Adding a new integration requires implementing specific interfaces and services to communicate with the external service.
Each integration follows a consistent pattern:
IssueServiceInterface, which defines the required methods for communicating with external services.IssueServiceInterface.Create a new directory under src/app/features/issue/providers/ for your integration, for example my-provider/.
Based on existing integrations, you'll need to create:
my-provider.model.ts - Define your provider's configuration and data structuresmy-provider-issue.model.ts - Define issue-specific data structuresExample from GitHub:
// github.model.ts
import { BaseIssueProviderCfg } from '../../issue.model';
export interface GithubCfg extends BaseIssueProviderCfg {
repo: string;
token?: string;
}
my-provider-api.service.ts - Handle API communicationmy-provider-common-interfaces.service.ts - Implement the IssueServiceInterfaceExample API service structure:
@Injectable({
providedIn: 'root',
})
export class MyProviderApiService {
// HTTP communication methods
getById$(issueId: string, cfg: MyProviderCfg): Observable<MyProviderIssue> {
// Implementation
}
searchIssues$(searchTerm: string, cfg: MyProviderCfg): Observable<MyProviderIssue[]> {
// Implementation
}
}
Example Common Interfaces Service structure:
@Injectable({
providedIn: 'root',
})
export class MyProviderCommonInterfacesService implements IssueServiceInterface {
// Implement all required methods from IssueServiceInterface
isEnabled(cfg: MyProviderCfg): boolean {
// Implementation
}
// Other required methods...
}
my-provider.const.ts - Define constants and default configurationsExample:
import { ConfigFormSection } from '../../../config/global-config.model';
export const MY_PROVIDER_INITIAL_POLL_DELAY = 5000;
export const MY_PROVIDER_POLL_INTERVAL = 5 * 60 * 1000;
export const DEFAULT_MY_PROVIDER_CFG: MyProviderCfg = {
isEnabled: false,
// Other default values
};
export const MY_PROVIDER_CONFIG_FORM_SECTION: ConfigFormSection = {
// Form configuration
};
is-my-provider-enabled.util.ts - Helper for checking if the provider is enabledExample:
import { MyProviderCfg } from './my-provider.model';
export const isMyProviderEnabled = (cfg: MyProviderCfg): boolean => {
return cfg && cfg.isEnabled && // other conditions;
};
The key interface methods that must be implemented include:
// MANDATORY
isEnabled(cfg: IssueIntegrationCfg): boolean;
testConnection$(cfg: IssueIntegrationCfg): Observable<boolean>;
pollTimer$: Observable<number>;
issueLink$(issueId: string | number, issueProviderId: string): Observable<string>;
getById$(id: string | number, issueProviderId: string): Observable<IssueData | null>;
getAddTaskData(issueData: IssueDataReduced): Partial<Task> & { title: string };
searchIssues$(searchTerm: string, issueProviderId: string): Observable<SearchResultItem[]>;
getFreshDataForIssueTask(task: Task): Promise<{ taskChanges: Partial<Task>; issue: IssueData; issueTitle: string; } | null>;
getFreshDataForIssueTasks(tasks: Task[]): Promise<{ task: Task; taskChanges: Partial<Task>; issue: IssueData; }[]>;
// OPTIONAL
getMappedAttachments?(issueData: IssueData): TaskAttachment[];
getNewIssuesToAddToBacklog?(issueProviderId: string, allExistingIssueIds: number[] | string[]): Promise<IssueDataReduced[]>;
You'll need to update several core files to register your new integration:
issue.model.tsAdd your provider to the BuiltInIssueProviderKey type:
export type BuiltInIssueProviderKey =
| 'JIRA'
| 'GITLAB'
| 'CALDAV'
| 'ICAL'
| 'OPEN_PROJECT'
| 'GITEA'
| 'TRELLO'
| 'REDMINE'
| 'LINEAR'
| 'CLICKUP'
| 'AZURE_DEVOPS'
| 'NEXTCLOUD_DECK'
| 'MY_PROVIDER'; // Add your provider here
Add your provider configuration to IssueIntegrationCfg:
export type IssueIntegrationCfg =
| JiraCfg
| GithubCfg
| GitlabCfg
| CaldavCfg
| CalendarProviderCfg
| OpenProjectCfg
| GiteaCfg
| RedmineCfg
| MyProviderCfg; // Add your provider here
Update IssueIntegrationCfgs interface:
export interface IssueIntegrationCfgs {
// should be the same as key IssueProviderKey
JIRA?: JiraCfg;
GITHUB?: GithubCfg;
GITLAB?: GitlabCfg;
CALDAV?: CaldavCfg;
CALENDAR?: CalendarProviderCfg;
OPEN_PROJECT?: OpenProjectCfg;
GITEA?: GiteaCfg;
REDMINE?: RedmineCfg;
MY_PROVIDER?: MyProviderCfg; // Add your provider here
}
Update IssueProvider type:
export type IssueProvider =
| IssueProviderJira
| IssueProviderGithub
| IssueProviderGitlab
| IssueProviderCaldav
| IssueProviderCalendar
| IssueProviderOpenProject
| IssueProviderGitea
| IssueProviderRedmine
| IssueProviderMyProvider; // Add your provider here
issue.const.tsAdd your provider type constant:
export const MY_PROVIDER_TYPE: IssueProviderKey = 'MY_PROVIDER';
Add your provider to ISSUE_PROVIDER_TYPES:
export const ISSUE_PROVIDER_TYPES: BuiltInIssueProviderKey[] = [
GITLAB_TYPE,
JIRA_TYPE,
CALDAV_TYPE,
ICAL_TYPE,
OPEN_PROJECT_TYPE,
GITEA_TYPE,
TRELLO_TYPE,
REDMINE_TYPE,
LINEAR_TYPE,
CLICKUP_TYPE,
AZURE_DEVOPS_TYPE,
NEXTCLOUD_DECK_TYPE,
MY_PROVIDER_TYPE, // Add your provider here
];
Update DEFAULT_ISSUE_PROVIDER_CFGS:
export const DEFAULT_ISSUE_PROVIDER_CFGS: IssueIntegrationCfgs = {
JIRA: DEFAULT_JIRA_CFG,
GITHUB: DEFAULT_GITHUB_CFG,
GITLAB: DEFAULT_GITLAB_CFG,
CALDAV: DEFAULT_CALDAV_CFG,
CALENDAR: DEFAULT_CALENDAR_CFG,
OPEN_PROJECT: DEFAULT_OPEN_PROJECT_CFG,
GITEA: DEFAULT_GITEA_CFG,
REDMINE: DEFAULT_REDMINE_CFG,
MY_PROVIDER: DEFAULT_MY_PROVIDER_CFG, // Add your provider here
};
Update ISSUE_PROVIDER_FORM_CFGS_MAP:
export const ISSUE_PROVIDER_FORM_CFGS_MAP: Record<IssueProviderKey, ConfigFormSection> = {
JIRA: JIRA_CONFIG_FORM_SECTION,
GITHUB: GITHUB_CONFIG_FORM_SECTION,
GITLAB: GITLAB_CONFIG_FORM_SECTION,
CALDAV: CALDAV_CONFIG_FORM_SECTION,
CALENDAR: CALENDAR_FORM_CFG_NEW,
OPEN_PROJECT: OPEN_PROJECT_CONFIG_FORM_SECTION,
GITEA: GITEA_CONFIG_FORM_SECTION,
REDMINE: REDMINE_CONFIG_FORM_SECTION,
MY_PROVIDER: MY_PROVIDER_CONFIG_FORM_SECTION, // Add your provider here
};
Depending on your integration, you may need to create UI components:
my-provider/my-provider-issue-content/ directorymy-provider/my-provider-issue-header/ directoryThe IssueService uses a provider factory pattern. Ensure your provider service is properly injected and registered.
npm run startFrontendIssueServiceInterfaceOnce your integration is working, please consider submitting it back to the Super Productivity project as a pull request!