@atlassian/atlassian-connect-express
Version:
Library for building Atlassian Add-ons on top of Express
299 lines (258 loc) • 10.3 kB
TypeScript
import * as events from "node:events";
import * as fs from "node:fs";
import * as querystring from "node:querystring";
import * as express from "express";
import * as request from "request";
import * as sequelize from "sequelize";
type Console = typeof console;
declare namespace AddOnFactory {
// Actual exports of lib/index.js
export interface Descriptor {
key: string;
name: string;
description: string;
vendor: {
name: string;
url: string;
};
baseUrl: string;
links: {
self: string;
homepage: string;
};
authentication: {
type: string;
};
scopes: string[];
}
export interface Options {
config: {
descriptorTransformer?: (descriptor: Partial<Descriptor>, config: Config) => Descriptor
development?: Partial<ConfigOptions>;
production?: Partial<ConfigOptions>;
}
}
export interface ConfigOptions {
environment: string;
port: string;
store: {
adapter?: string,
type?: string,
url?: string,
storage?: string
};
expressErrorHandling: boolean;
errorTemplate: boolean;
errorTemplateName: string;
errorTemplateObject: Record<string, unknown>;
validateDescriptor: boolean;
localBaseUrl: string;
jwt: {
validityInMinutes: number;
};
product: string;
hosts: string[];
maxTokenAge: number;
userAgent: string;
watch: boolean;
}
export interface Config {
port(): string
environment(): string;
store(): {
adapter: string,
type: string
};
expressErrorHandling(): boolean;
errorTemplate(): boolean;
getErrorTemplateName(): string;
getErrorTemplateObject(): Record<string, unknown>;
validateDescriptor(): boolean;
localBaseUrl(): string;
jwt(): {
validityInMinutes: number;
};
product(): string;
hosts(): string[];
maxTokenAge(): number;
userAgent(): string;
}
export interface KVStore {
del(key: string): Promise<void>;
get<T = unknown>(key: string): Promise<T>;
set<T = unknown>(key: string, value: T | string): Promise<T>;
}
export interface StoreAdapter {
del(key: string, clientKey: string): Promise<void>;
get<T = unknown>(key: string, clientKey: string): Promise<T>;
/**
* Used to set data under `key` for a given client.
* Do not use this to save clientInfo, use `saveInstallation` instead, as it handles associating Forge installations.
*/
set<T = unknown>(key: string, value: T | string, clientKey: string): Promise<T>;
getAllClientInfos(): Promise<ClientInfo[]>;
saveInstallation(value: ClientInfo, clientKey: string): Promise<ClientInfo>;
getClientSettingsForForgeInstallation(forgeInstallationId: string): Promise<ForgeInstallationClientSettings>;
deleteAssociation(forgeInstallationId: string): Promise<void>;
forForgeInstallation(installationId: string): KVStore;
}
export type ForgeInstallationClientSettings = {
clientKey: string;
installationId: string;
} | null;
export const DESCRIPTOR_FILENAME: "atlassian-connect.json";
export const store: {
register(
adapterKey: string,
factory: (logger: Console, opts: unknown) => StoreAdapter
): void;
}
export type JiraPermissionsQuery = {
project?: string[]
global?: string[]
};
export type ConfluencePermissionsQuery = {
application?: string[]
content?: string
};
export type ModifyRequestArgsOptions = Omit<request.Options, 'form' | 'qs'> & {
/** @deprecated Use multipartFormData instead */
form?: request.Options['form'];
multipartFormData?: Record<string, unknown | unknown[]>;
urlEncodedFormData?: Record<string, unknown>;
qs?: querystring.ParsedUrlQueryInput;
}|URL|string;
export type ModifyArgsOutput<
TOptions extends ModifyRequestArgsOptions,
TCallback extends request.RequestCallback
> = TCallback extends request.RequestCallback
? [TOptions, TCallback]
: [TCallback];
export type HostClientArgs<
TOptions extends ModifyRequestArgsOptions,
TCallback extends request.RequestCallback
> = [
TOptions, request.Headers, TCallback, string
];
export type BearerToken = {
access_token: string;
token_type: string;
expires_in: number;
};
export type RequestOptions = ModifyRequestArgsOptions & {
/** don't authenticate the request */
anonymous?: boolean;
}
export type ForgeAppToken = {
appToken: string;
apiBaseUrl: string;
}
// This is a class that isn't exported, so we don't want to export a class that's not importable at runtime
// We declare it as an interface so consumers can do module augmentation though, instead of `declare class X` and exporting `InstanceType<typeof X>.
export interface HostClient {
addon: AddOn;
context: boolean;
clientKey: string;
oauth2: unknown;
userKey?: string; // for impersonatingClient
asUser(userKey: string): HostClient;
asUserByAccountId: (userAccountId: string|number) => HostClient;
createJwtPayload: (req: request.Request) => string;
getUserBearerToken: (scopes: string[], clientSettings: ClientInfo) => Promise<BearerToken>;
getBearerToken: (clientSettings: ClientInfo) => Promise<BearerToken>;
defaults(): request.Request;
cookie(): request.Cookie;
jar(): request.CookieJar;
modifyArgs<TOptions extends ModifyRequestArgsOptions = ModifyRequestArgsOptions, TCallback extends request.RequestCallback = request.RequestCallback>(...args: HostClientArgs<TOptions, TCallback>): ModifyArgsOutput<TOptions, TCallback>;
get: <T = unknown, TOptions extends RequestOptions = RequestOptions>(options: TOptions, callback?: request.RequestCallback) => Promise<T>;
post: <T = unknown, TOptions extends RequestOptions = RequestOptions>(options: TOptions, callback?: request.RequestCallback) => Promise<T>;
put: <T = unknown, TOptions extends RequestOptions = RequestOptions>(options: TOptions, callback?: request.RequestCallback) => Promise<T>;
del: <T = unknown, TOptions extends RequestOptions = RequestOptions>(options: TOptions, callback?: request.RequestCallback) => Promise<T>;
head: <T = unknown, TOptions extends RequestOptions = RequestOptions>(options: TOptions, callback?: request.RequestCallback) => Promise<T>;
patch: <T = unknown, TOptions extends RequestOptions = RequestOptions>(options: TOptions, callback?: request.RequestCallback) => Promise<T>;
}
// This is a class that isn't exported, so we don't want to export a class that's not importable at runtime
// We declare it as an interface so consumers can do module augmentation though, instead of `declare class X` and exporting `InstanceType<typeof X>.
export interface AddOn extends events.EventEmitter {
verifyInstallation(): express.RequestHandler;
authenticateInstall(): express.RequestHandler;
postInstallation(): (request: express.Request, response: express.Response) => void;
middleware(): express.RequestHandler;
authenticateForge(): express.RequestHandler;
getForgeAppToken(): Promise<ForgeAppToken>;
associateConnect(): express.RequestHandler;
authenticate(skipQshVerification?: boolean): express.RequestHandler;
authorizeJira(permissions: JiraPermissionsQuery): express.RequestHandler;
authorizeConfluence(permissions: ConfluencePermissionsQuery): express.RequestHandler;
loadClientInfo(clientKey: string): Promise<ClientInfo>;
checkValidToken(): express.RequestHandler;
register() : Promise<void>;
key: string;
name: string;
config: Config;
app: express.Application;
deregister(): Promise<void>;
descriptor: Descriptor;
descriptorExists: boolean;
schema: sequelize.Sequelize;
settings: StoreAdapter;
shouldDeregister(): boolean;
shouldRegister(): boolean;
validateDescriptor(): {
type: string;
message: string;
validationResults: {
module: string;
description: string;
value?: unknown;
validValues?: string[];
}[]
}[];
watcher: fs.FSWatcher;
/**
* Reloads AddOn descriptor file
*/
reloadDescriptor(): void;
/* eslint-disable @typescript-eslint/unified-signatures */
/**
* @param opts a clientKey, and optionally a userKey
* @deprecated to call with userKey, pass userAccountId instead.
*/
httpClient(opts: { clientKey: string, userKey?: string }): HostClient;
/**
* @param reqOrOpts either an expressRequest object or an object containing a clientKey, and optionally a userAccountId
* @returns HostClient a httpClient
*/
httpClient(
reqOrOpts: express.Request | { clientKey: string; userAccountId?: string }
): HostClient;
/* eslint-enable @typescript-eslint/unified-signatures */
}
export interface FileNames {
descriptorFilename?: string;
configFileName?: string;
}
export interface ClientInfo {
key: string,
clientKey: string,
publicKey: string
sharedSecret: string,
serverVersion: string,
pluginsVersion: string,
baseUrl: string,
displayUrl?: string;
productType: string,
description: string,
eventType: string,
oauthClientId?: string
}
export type AddOnFactory = typeof AddOnFactory;
} // End namespace AddOnFactory
// Declaring the function then a namespace is how to support the CJS export pattern we use in lib/index.js without using `export default` and requiring TS consumers to use `esModuleInterop: true`
// It also means that any types we want to expose to consumers have to be exported
// https://www.typescriptlang.org/docs/handbook/declaration-files/templates/module-d-ts.html#common-commonjs-patterns
// https://www.typescriptlang.org/docs/handbook/declaration-files/templates/module-function-d-ts.html
export = AddOnFactory;
// Overload definition to properly type how fileNames can be omitted.
declare function AddOnFactory(app: express.Application, opts?: AddOnFactory.Options, logger?: Console, callback?: () => void): AddOnFactory.AddOn;
declare function AddOnFactory(app: express.Application, opts?: AddOnFactory.Options, logger?: Console, fileNames?: AddOnFactory.FileNames, callback?: () => void): AddOnFactory.AddOn;