From f711176731ab2671c6903d0d3452aa4320138e68 Mon Sep 17 00:00:00 2001 From: "Mike P. Sinn" Date: Sun, 2 Mar 2025 00:03:09 -0600 Subject: [PATCH] docs: add comprehensive guide for creating new issue tracker integrations --- docs/add-new-integration.md | 303 ++++++++++++++++++++++++++++++++++++ 1 file changed, 303 insertions(+) create mode 100644 docs/add-new-integration.md diff --git a/docs/add-new-integration.md b/docs/add-new-integration.md new file mode 100644 index 000000000..4c956a9d2 --- /dev/null +++ b/docs/add-new-integration.md @@ -0,0 +1,303 @@ +# Adding a New Integration to Super Productivity + +This guide explains how to add a new issue tracker integration to Super Productivity. + +## Overview + +Super Productivity supports multiple issue tracker integrations (called "Issue Providers" in the codebase), including GitHub, GitLab, Jira, and others. Adding a new integration requires implementing specific interfaces and services to communicate with the external service. + +## Integration Architecture + +Each integration follows a consistent pattern: + +1. **Interface**: All integrations implement the `IssueServiceInterface`, which defines the required methods for communicating with external services. +2. **Provider-specific Models**: Each integration defines its own data structures. +3. **API Services**: Each integration has an API service that handles HTTP requests. +4. **Common Interfaces Service**: Each integration has a service that implements `IssueServiceInterface`. +5. **Configuration**: Each integration defines its configuration options. + +## Step-by-Step Guide + +### 1. Create the Provider Directory Structure + +Create a new directory under `src/app/features/issue/providers/` for your integration, for example `my-provider/`. + +### 2. Create Required Files + +Based on existing integrations, you'll need to create: + +#### Model Files + +- `my-provider.model.ts` - Define your provider's configuration and data structures +- `my-provider-issue.model.ts` - Define issue-specific data structures + +Example from GitHub: + +```typescript +// github.model.ts +import { BaseIssueProviderCfg } from '../../issue.model'; + +export interface GithubCfg extends BaseIssueProviderCfg { + repo: string; + token?: string; +} +``` + +#### Service Files + +- `my-provider-api.service.ts` - Handle API communication +- `my-provider-common-interfaces.service.ts` - Implement the `IssueServiceInterface` + +Example API service structure: + +```typescript +@Injectable({ + providedIn: 'root', +}) +export class MyProviderApiService { + // HTTP communication methods + getById$(issueId: string, cfg: MyProviderCfg): Observable { + // Implementation + } + + searchIssues$(searchTerm: string, cfg: MyProviderCfg): Observable { + // Implementation + } +} +``` + +Example Common Interfaces Service structure: + +```typescript +@Injectable({ + providedIn: 'root', +}) +export class MyProviderCommonInterfacesService implements IssueServiceInterface { + // Implement all required methods from IssueServiceInterface + + isEnabled(cfg: MyProviderCfg): boolean { + // Implementation + } + + // Other required methods... +} +``` + +#### Constants File + +- `my-provider.const.ts` - Define constants and default configurations + +Example: + +```typescript +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 +}; +``` + +#### Utility Files + +- `is-my-provider-enabled.util.ts` - Helper for checking if the provider is enabled + +Example: + +```typescript +import { MyProviderCfg } from './my-provider.model'; + +export const isMyProviderEnabled = (cfg: MyProviderCfg): boolean => { + return cfg && cfg.isEnabled && // other conditions; +}; +``` + +### 3. Implement the IssueServiceInterface + +The key interface methods that must be implemented include: + +```typescript +// MANDATORY +isEnabled(cfg: IssueIntegrationCfg): boolean; +testConnection$(cfg: IssueIntegrationCfg): Observable; +pollTimer$: Observable; +issueLink$(issueId: string | number, issueProviderId: string): Observable; +getById$(id: string | number, issueProviderId: string): Observable; +getAddTaskData(issueData: IssueDataReduced): Partial & { title: string }; +searchIssues$(searchTerm: string, issueProviderId: string): Observable; +getFreshDataForIssueTask(task: Task): Promise<{ taskChanges: Partial; issue: IssueData; issueTitle: string; } | null>; +getFreshDataForIssueTasks(tasks: Task[]): Promise<{ task: Task; taskChanges: Partial; issue: IssueData; }[]>; + +// OPTIONAL +getMappedAttachments?(issueData: IssueData): TaskAttachment[]; +getNewIssuesToAddToBacklog?(issueProviderId: string, allExistingIssueIds: number[] | string[]): Promise; +``` + +### 4. Update Core Files + +You'll need to update several core files to register your new integration: + +#### 1. Update `issue.model.ts` + +Add your provider to the `IssueProviderKey` type: + +```typescript +export type IssueProviderKey = + | 'JIRA' + | 'GITHUB' + | 'GITLAB' + | 'CALDAV' + | 'ICAL' + | 'OPEN_PROJECT' + | 'GITEA' + | 'REDMINE' + | 'MY_PROVIDER'; // Add your provider here +``` + +Add your provider configuration to `IssueIntegrationCfg`: + +```typescript +export type IssueIntegrationCfg = + | JiraCfg + | GithubCfg + | GitlabCfg + | CaldavCfg + | CalendarProviderCfg + | OpenProjectCfg + | GiteaCfg + | RedmineCfg + | MyProviderCfg; // Add your provider here +``` + +Update `IssueIntegrationCfgs` interface: + +```typescript +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: + +```typescript +export type IssueProvider = + | IssueProviderJira + | IssueProviderGithub + | IssueProviderGitlab + | IssueProviderCaldav + | IssueProviderCalendar + | IssueProviderOpenProject + | IssueProviderGitea + | IssueProviderRedmine + | IssueProviderMyProvider; // Add your provider here +``` + +#### 2. Update `issue.const.ts` + +Add your provider type constant: + +```typescript +export const MY_PROVIDER_TYPE: IssueProviderKey = 'MY_PROVIDER'; +``` + +Add your provider to `ISSUE_PROVIDER_TYPES`: + +```typescript +export const ISSUE_PROVIDER_TYPES: IssueProviderKey[] = [ + GITLAB_TYPE, + GITHUB_TYPE, + JIRA_TYPE, + CALDAV_TYPE, + ICAL_TYPE, + OPEN_PROJECT_TYPE, + GITEA_TYPE, + REDMINE_TYPE, + MY_PROVIDER_TYPE, // Add your provider here +]; +``` + +Update `DEFAULT_ISSUE_PROVIDER_CFGS`: + +```typescript +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`: + +```typescript +export const ISSUE_PROVIDER_FORM_CFGS_MAP: Record = { + 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 +}; +``` + +### 5. Create UI Components (Optional) + +Depending on your integration, you may need to create UI components: + +- Issue display and content components in `my-provider/my-provider-issue-content/` directory +- Header components in `my-provider/my-provider-issue-header/` directory +- Configuration components if needed + +### 6. Register the Provider in the Issue Service + +The `IssueService` uses a provider factory pattern. Ensure your provider service is properly injected and registered. + +## Testing Your Integration + +1. Run the app with `npm run startFrontend` +2. Navigate to the settings page +3. Add a new integration of your provider type +4. Test the connection and functionality + +## Tips and Best Practices + +1. **Study Existing Integrations**: Use GitHub or GitLab integrations as reference implementations +2. **Error Handling**: Implement robust error handling for API failures +3. **Polling**: Consider rate limits when implementing polling +4. **Authentication**: Securely handle authentication tokens +5. **User Experience**: Make configuration and usage as simple as possible + +## Troubleshooting + +- Check browser console for errors +- Verify correct implementation of the `IssueServiceInterface` +- Ensure all model types are correctly defined +- Verify correct registration in all required files + +## Contributing Back + +Once your integration is working, please consider submitting it back to the Super Productivity project as a pull request!