Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
275 changes: 275 additions & 0 deletions src/adapters/git.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
import type { RoutingOptions } from '../utils/router';

import { Router } from '../utils';

// ──────────────────────────────────────────────
// Types
// ──────────────────────────────────────────────

export interface GitStatusReadOutView {
currentBranch?: string | null;
}

export interface GitIntegrationReadOutView {
privateKeySpec?: string | null;
publicKey?: string | null;
uri?: string | null;
publicKeySpec?: string | null;
algorithm?: string | null;
}

export interface GitIntegrationCreateInView {
privateKey: string;
privateKeySpec: unknown;
publicKey: string;
uri: string;
publicKeySpec: unknown;
algorithm: unknown;
}
Comment on lines +21 to +28

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The properties privateKeySpec, publicKeySpec, and algorithm are typed as unknown. Based on the example usage in the createIntegration function's JSDoc, these appear to be strings. Using string instead of unknown would improve type safety and developer experience for users of this adapter, as it would remove the need for type assertions.

Suggested change
export interface GitIntegrationCreateInView {
privateKey: string;
privateKeySpec: unknown;
publicKey: string;
uri: string;
publicKeySpec: unknown;
algorithm: unknown;
}
export interface GitIntegrationCreateInView {
privateKey: string;
privateKeySpec: string;
publicKey: string;
uri: string;
publicKeySpec: string;
algorithm: string;
}


export interface GitIntegrationUpdateInView {
privateKey?: string | null;
privateKeySpec: unknown;
publicKey?: string | null;
uri?: string | null;
publicKeySpec: unknown;
algorithm: unknown;
}
Comment on lines +30 to +37

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

In GitIntegrationUpdateInView, some properties (privateKeySpec, publicKeySpec, algorithm) are marked as required, while others are optional. For an update/PATCH operation, it's typical for all fields to be optional, allowing clients to update only the fields they need to change. The JSDoc @param integration Fields to update on the git integration also suggests this. Consider making all properties in this interface optional.

Additionally, these properties are typed as unknown. As with GitIntegrationCreateInView, they should likely be string for better type safety.

Suggested change
export interface GitIntegrationUpdateInView {
privateKey?: string | null;
privateKeySpec: unknown;
publicKey?: string | null;
uri?: string | null;
publicKeySpec: unknown;
algorithm: unknown;
}
export interface GitIntegrationUpdateInView {
privateKey?: string | null;
privateKeySpec?: string;
publicKey?: string | null;
uri?: string | null;
publicKeySpec?: string;
algorithm?: string;
}



// ──────────────────────────────────────────────
// Functions
// ──────────────────────────────────────────────

/**
* Retrieves the git integration configuration for the project.
* Base URL: GET `https://forio.com/api/v3/{ACCOUNT}/{PROJECT}/git`
*
* @example
* import { gitAdapter } from 'epicenter-libs';
* const integration = await gitAdapter.get();
*
* @param [optionals] Optional arguments; pass network call options overrides here.
* @returns promise that resolves to the git integration configuration
*/
export async function get(
optionals: RoutingOptions = {},
): Promise<GitIntegrationReadOutView> {
return new Router()
.get('/git', optionals)
.then(({ body }) => body);
}


/**
* Retrieves the current git status for the project.
* Base URL: GET `https://forio.com/api/v3/{ACCOUNT}/{PROJECT}/git/status`
*
* @example
* import { gitAdapter } from 'epicenter-libs';
* const status = await gitAdapter.getStatus();
* console.log(status.currentBranch);
*
* @param [optionals] Optional arguments; pass network call options overrides here.
* @returns promise that resolves to the git status, including the current branch
*/
export async function getStatus(
optionals: RoutingOptions = {},
): Promise<GitStatusReadOutView> {
return new Router()
.get('/git/status', optionals)
.then(({ body }) => body);
}


/**
* Checks out a branch in the project's git repository.
* Base URL: GET `https://forio.com/api/v3/{ACCOUNT}/{PROJECT}/git/checkout/{branch}`
*
* @example
* import { gitAdapter } from 'epicenter-libs';
* await gitAdapter.checkout('main');
*
* @param branch Name of the branch to check out
* @param [optionals] Optional arguments; pass network call options overrides here.
* @returns promise that resolves when the checkout is complete
*/
export async function checkout(
branch: string,
optionals: RoutingOptions = {},
): Promise<void> {
return new Router()
.get(`/git/checkout/${branch}`, optionals)
.then(({ body }) => body);
}


/**
* Resets the project's git repository, optionally to a specific branch.
* Base URL: DELETE `https://forio.com/api/v3/{ACCOUNT}/{PROJECT}/git/reset[/{branch}]`
*
* @example
* import { gitAdapter } from 'epicenter-libs';
* await gitAdapter.reset(); // reset current branch
* await gitAdapter.reset({ branch: 'main' }); // reset to 'main'
*
* @param [optionals] Optional arguments; pass network call options overrides here. Special arguments specific to this method are listed below if they exist.
* @param [optionals.branch] Branch to reset to; if omitted, resets the current branch
* @returns promise that resolves when the reset is complete
*/
export async function reset(
optionals: { branch?: string } & RoutingOptions = {},
): Promise<void> {
const { branch, ...routingOptions } = optionals;
return new Router()
.delete(`/git/reset${branch ? `/${branch}` : ''}`, routingOptions)
.then(({ body }) => body);
}


/**
* Creates a git integration for the project.
* Base URL: POST `https://forio.com/api/v3/{ACCOUNT}/{PROJECT}/git/integration`
*
* @example
* import { gitAdapter } from 'epicenter-libs';
* const integration = await gitAdapter.createIntegration({
* uri: 'git@github.com:myorg/myrepo.git',
* publicKey: '...',
* privateKey: '...',
* publicKeySpec: 'ssh-ed25519',
* privateKeySpec: 'OPENSSH',
* algorithm: 'Ed25519',
* });
*
* @param integration Git integration configuration to create
* @param [optionals] Optional arguments; pass network call options overrides here.
* @returns promise that resolves to the created git integration
*/
export async function createIntegration(
integration: GitIntegrationCreateInView,
optionals: RoutingOptions = {},
): Promise<GitIntegrationReadOutView> {
return new Router()
.post('/git/integration', {
body: integration,
...optionals,
}).then(({ body }) => body);
}


/**
* Updates the git integration for the project.
* Base URL: PATCH `https://forio.com/api/v3/{ACCOUNT}/{PROJECT}/git/integration`
*
* @example
* import { gitAdapter } from 'epicenter-libs';
* const integration = await gitAdapter.updateIntegration({
* uri: 'git@github.com:myorg/newrepo.git',
* publicKeySpec: 'ssh-ed25519',
* privateKeySpec: 'OPENSSH',
* algorithm: 'Ed25519',
* });
*
* @param integration Fields to update on the git integration
* @param [optionals] Optional arguments; pass network call options overrides here.
* @returns promise that resolves to the updated git integration
*/
export async function updateIntegration(
integration: GitIntegrationUpdateInView,
optionals: RoutingOptions = {},
): Promise<GitIntegrationReadOutView> {
return new Router()
.patch('/git/integration', {
body: integration,
...optionals,
}).then(({ body }) => body);
}


/**
* Removes the git integration for the project.
* Base URL: DELETE `https://forio.com/api/v3/{ACCOUNT}/{PROJECT}/git/integration`
*
* @example
* import { gitAdapter } from 'epicenter-libs';
* await gitAdapter.removeIntegration();
*
* @param [optionals] Optional arguments; pass network call options overrides here.
* @returns promise that resolves when the integration is removed
*/
export async function removeIntegration(
optionals: RoutingOptions = {},
): Promise<void> {
return new Router()
.delete('/git/integration', optionals)
.then(({ body }) => body);
}


/**
* Pushes local commits to the remote git repository.
* Base URL: POST `https://forio.com/api/v3/{ACCOUNT}/{PROJECT}/git/push`
*
* @example
* import { gitAdapter } from 'epicenter-libs';
* await gitAdapter.push({ message: 'Update simulation data' });
*
* @param [optionals] Optional arguments; pass network call options overrides here. Special arguments specific to this method are listed below if they exist.
* @param [optionals.message] Commit message
* @param [optionals.password] Password for authentication
* @param [optionals.force] Force-push, bypassing non-fast-forward checks
* @returns promise that resolves when the push is complete
*/
export async function push(
optionals: {
message?: string | null;
password?: string | null;
force?: boolean | null;
} & RoutingOptions = {},
): Promise<void> {
const { message, password, force, ...routingOptions } = optionals;
return new Router()
.withSearchParams({ force })
.post('/git/push', {
body: { message, password },
...routingOptions,
}).then(({ body }) => body);
}


/**
* Pulls changes from the remote git repository into the project.
* Base URL: POST `https://forio.com/api/v3/{ACCOUNT}/{PROJECT}/git/pull`
*
* @example
* import { gitAdapter } from 'epicenter-libs';
* await gitAdapter.pull({ force: true, confirm: true });
*
* @param [optionals] Optional arguments; pass network call options overrides here. Special arguments specific to this method are listed below if they exist.
* @param [optionals.password] Password for authentication
* @param [optionals.force] Force the pull, overwriting local changes
* @param [optionals.confirm] Set the `X-Forio-Confirmation` header to confirm an overwrite
* @returns promise that resolves when the pull is complete
*/
export async function pull(
optionals: {
password?: string | null;
force?: boolean | null;
confirm?: boolean | null;
} & RoutingOptions = {},
): Promise<void> {
const { password, force, confirm, headers: headersOverride, ...routingOptions } = optionals;
const headers = Object.assign(
{},
headersOverride,
confirm ? { 'X-Forio-Confirmation': true } : {},
);
return new Router()
.withSearchParams({ force })
.post('/git/pull', {
body: { password },
headers,
...routingOptions,
}).then(({ body }) => body);
}
2 changes: 2 additions & 0 deletions src/adapters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import * as dailyAdapter from './daily';
import * as walletAdapter from './wallet';
import { default as cometdAdapter } from './cometd';
import { default as Channel } from './channel';
import * as gitAdapter from './git';

export {
accountAdapter,
Expand Down Expand Up @@ -54,4 +55,5 @@ export {
dailyAdapter,
walletAdapter,
Channel,
gitAdapter,
};
1 change: 1 addition & 0 deletions src/epicenter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ export {
walletAdapter,
Channel,
cometdAdapter,
gitAdapter,
} from './adapters';

/* APIs */
Expand Down
1 change: 1 addition & 0 deletions tests/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ export const {
videoAdapter,
vonageAdapter,
cometdAdapter,
gitAdapter,
} = globalThis.epicenter || {};

export const testedMethods = new Set();
Expand Down
Loading
Loading