Addon capabilities

DataSources

Connect external tools to read tasks and synchronize time entries.

Open Markdown

A datasource connects Mr. Tick to a tool that provides tasks and receives time entries. It translates information from that tool into the format the application understands.

Declare capabilities: [auth, dataSource] for this kind of integration. Connecting an account alone does not make an addon a datasource: an activity observer may need an account without providing tasks.

Implementation

IDataSource supplies getConnectionSchema() and createInstance(context). Its instance contains only tasksProvider, timeEntriesProvider, membersProvider and metadataProvider. Authentication is registered separately:

import {
  type AddonContext,
  type AddonConnectionAuthenticator,
  type ConnectionProfileDescriptor,
  type AppError,
  Either,
  type IDataSource,
} from '@mr-tick/sdk'

export function registerDataSource(
  context: AddonContext,
  dataSource: IDataSource,
  profiles: ConnectionProfileDescriptor[],
  authenticator: AddonConnectionAuthenticator,
): Either<AppError, void> {
  const registered = context.contributions.connections.registerProfiles(profiles)

  if (registered.isFailure()) return registered.forwardFailure()

  const authenticated = context.contributions.connections.registerAuthenticator(authenticator)

  if (authenticated.isFailure()) {
    context.contributions.connections.unregisterProfiles()

    return authenticated.forwardFailure()
  }

  context.contributions.dataSources.register(dataSource)

  return Either.success()
}

Create each set of providers from its own DataSourceContext: scope, configuration, credentials and HTTP client. Never share mutable HTTP configuration between connections. No authentication strategy or health method belongs in IDataSourceInstance.

OperationResult inside Promise<Either<AppError, T>>
Task pullTaskDTO[]
Time-entry pullTimeEntryPullPageDTO
Task/time-entry findByIdDTO or null
create / updateResult with confirmed remote ID; time-entry results may include canonical entry
deletevoid

Time-entry pull returns items, checkpoint, hasMore and snapshotId; an array is insufficient. createTimeEntrySnapshotPage paginates a complete validated snapshot. Detect incomplete/inconsistent remote pagination before reporting success.

Advertise timeEntryCreateIdempotency according to backend guarantees: native, reconcilable or none. Expose updateConditional only when the server compares the expected state and writes atomically; GET followed by PUT does not provide that guarantee.

Every IHttpClient request requires responseValidator, narrowing JsonValue to the external response. JSON parsing and generic type arguments do not validate payloads. Handle legitimate nulls/omitted fields and preserve status codes and server messages. Addons use provider DTOs, not private domain entities.

See authentication, OAuth PKCE and vault and events.

Reference implementation: Redmine addon, including HTTP and Docker integration tests.

On this page