mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-01-23 02:36:05 +00:00
303 lines
8.5 KiB
Markdown
303 lines
8.5 KiB
Markdown
# 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<MyProviderIssue> {
|
|
// Implementation
|
|
}
|
|
|
|
searchIssues$(searchTerm: string, cfg: MyProviderCfg): Observable<MyProviderIssue[]> {
|
|
// 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<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[]>;
|
|
```
|
|
|
|
### 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<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
|
|
};
|
|
```
|
|
|
|
### 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!
|