Skip to content

Commit

Permalink
🗝 repo providers now make storageKeys (#723)
Browse files Browse the repository at this point in the history
* 🗝 repo providers now make storageKeys
* 💚 tests
  • Loading branch information
stevejpurves authored Dec 20, 2023
1 parent c085abe commit 0759842
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 80 deletions.
2 changes: 1 addition & 1 deletion packages/core/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function makeSavedSessionOptions(opts: SavedSessionOptions): Required<Sav
return {
enabled: true,
maxAge: 86400,
storagePrefix: 'thebe-binder-',
storagePrefix: 'thebe-binder',
...opts,
};
}
Expand Down
12 changes: 7 additions & 5 deletions packages/core/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import type { LiteServerConfig } from 'thebe-lite';
import type { IRenderMimeRegistry } from '@jupyterlab/rendermime';
import type { StatusEvent } from './events';
import { WELL_KNOWN_REPO_PROVIDERS, makeBinderUrls } from './url';
import { getExistingServer, makeStorageKey, saveServerInfo } from './sessions';
import { getExistingServer, makeDefaultStorageKey, saveServerInfo } from './sessions';
import {
KernelManager,
KernelSpecAPI,
Expand Down Expand Up @@ -172,7 +172,9 @@ class ThebeServer implements ServerRuntime, ServerRestAPI {
async clearSavedBinderSessions() {
const url = this.sessionManager?.serverSettings?.baseUrl;
if (url)
window.localStorage.removeItem(makeStorageKey(this.config.savedSessions.storagePrefix, url));
window.localStorage.removeItem(
makeDefaultStorageKey(this.config.savedSessions.storagePrefix, url),
);
}

/**
Expand Down Expand Up @@ -299,13 +301,13 @@ class ThebeServer implements ServerRuntime, ServerRestAPI {
}

makeBinderUrls() {
return makeBinderUrls(this.config.binder, this.repoProviders ?? WELL_KNOWN_REPO_PROVIDERS);
return makeBinderUrls(this.config, this.repoProviders ?? WELL_KNOWN_REPO_PROVIDERS);
}

async checkForSavedBinderSession() {
try {
const { build } = makeBinderUrls(
this.config.binder,
this.config,
this.repoProviders ?? WELL_KNOWN_REPO_PROVIDERS,
);
return getExistingServer(this.config.savedSessions, build);
Expand Down Expand Up @@ -337,7 +339,7 @@ class ThebeServer implements ServerRuntime, ServerRestAPI {
this.repoProviders = [...WELL_KNOWN_REPO_PROVIDERS, ...(customProviders ?? [])];

try {
this.binderUrls = makeBinderUrls(this.config.binder, this.repoProviders);
this.binderUrls = makeBinderUrls(this.config, this.repoProviders);
} catch (err: any) {
this.events.triggerError({
status: ErrorStatusEvent.error,
Expand Down
14 changes: 7 additions & 7 deletions packages/core/src/sessions.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { KernelAPI, ServerConnection } from '@jupyterlab/services';
import type { SavedSessionInfo, SavedSessionOptions, ServerSettings } from './types';

export function makeStorageKey(storagePrefix: string, url: string) {
export function makeDefaultStorageKey(storagePrefix: string, url: string) {
const urlObj = new URL(url);
// ignore the query string and hash
return `${storagePrefix}-${urlObj.origin + urlObj.pathname}`;
}

export function removeServerInfo(savedSession: Required<SavedSessionOptions>, url: string) {
window.localStorage.removeItem(makeStorageKey(savedSession.storagePrefix, url));
window.localStorage.removeItem(makeDefaultStorageKey(savedSession.storagePrefix, url));
}

export function updateLastUsedTimestamp(savedSession: Required<SavedSessionOptions>, url: string) {
const storageKey = makeStorageKey(savedSession.storagePrefix, url);
const storageKey = makeDefaultStorageKey(savedSession.storagePrefix, url);
const saved = window.localStorage.getItem(storageKey);
if (!saved) return;
const obj = JSON.parse(saved);
Expand All @@ -29,7 +29,7 @@ export function saveServerInfo(
// save the current connection url+token to reuse later
const { baseUrl, token, wsUrl } = serverSettings;
window.localStorage.setItem(
makeStorageKey(savedSession.storagePrefix, url),
makeDefaultStorageKey(savedSession.storagePrefix, url),
JSON.stringify({
id,
baseUrl,
Expand All @@ -49,7 +49,7 @@ export async function getExistingServer(
url: string,
): Promise<SavedSessionInfo | null> {
if (!savedSessionOptions.enabled) return null;
const storageKey = makeStorageKey(savedSessionOptions.storagePrefix, url);
const storageKey = makeDefaultStorageKey(savedSessionOptions.storagePrefix, url);
const storedInfoJSON = window.localStorage.getItem(storageKey);
if (storedInfoJSON == null) {
console.debug('thebe:getExistingServer No session saved in ', storageKey);
Expand Down Expand Up @@ -120,6 +120,6 @@ export function clearAllSavedSessions(storagePrefix = 'thebe-binder') {
* @param url
*/
export function clearSavedSession(storagePrefix = 'thebe-binder', url = '') {
console.debug(`thebe:clearSavedSession - removing ${makeStorageKey(storagePrefix, url)}`);
window.localStorage.removeItem(makeStorageKey(storagePrefix, url));
console.debug(`thebe:clearSavedSession - removing ${makeDefaultStorageKey(storagePrefix, url)}`);
window.localStorage.removeItem(makeDefaultStorageKey(storagePrefix, url));
}
4 changes: 3 additions & 1 deletion packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { IOutput, IError } from '@jupyterlab/nbformat';
import type { IRenderMimeRegistry } from '@jupyterlab/rendermime';
import type ThebeServer from './server';
import type { ServerStatusEvent } from './events';
import type { Config } from './config';

export type CellKind = 'code' | 'markdown';

Expand Down Expand Up @@ -41,12 +42,13 @@ export interface CoreOptions {

export interface RepoProviderSpec {
name: string;
makeUrls: (opts: BinderOptions) => BinderUrlSet;
makeUrls: (config: Config) => BinderUrlSet;
}

export interface BinderUrlSet {
build: string;
launch: string;
storageKey?: string;
}

export type WellKnownRepoProvider = 'git' | 'github' | 'gitlab' | 'gist';
Expand Down
90 changes: 48 additions & 42 deletions packages/core/src/url.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
import type { BinderOptions, BinderUrlSet, RepoProviderSpec } from './types';
import type { Config } from './config';
import { makeDefaultStorageKey } from './sessions';
import type { BinderUrlSet, RepoProviderSpec } from './types';

function makeDefaultBuildSpec(storagePrefix: string, binderUrl: string, stub: string) {
const build = `${binderUrl}/build/${stub}`;
const launch = `${binderUrl}/v2/${stub}`;

return {
build,
launch,
storageKey: makeDefaultStorageKey(storagePrefix, build),
};
}

/**
* Make a binder url for git providers
Expand All @@ -10,16 +23,13 @@ import type { BinderOptions, BinderUrlSet, RepoProviderSpec } from './types';
* @param opts BinderOptions
* @returns a binder compatible url
*/
function makeGitUrls(opts: BinderOptions) {
if (!opts.repo) throw Error('repo is required for git provider');
const { repo, binderUrl, ref } = opts;
function makeGitUrls(config: Config) {
if (!config.binder.repo) throw Error('repo is required for git provider');
const { repo, binderUrl, ref } = config.binder;
const encodedRepo = encodeURIComponent(repo.replace(/(^\/)|(\/?$)/g, ''));
const base = binderUrl?.replace(/(\/?$)/g, '');
const stub = `git/${encodedRepo}/${ref ?? 'HEAD'}`;
return {
build: `${base}/build/${stub}`,
launch: `${base}/v2/${stub}`,
};
return makeDefaultBuildSpec(config.savedSessions.storagePrefix, base, stub);
}

/**
Expand All @@ -33,17 +43,16 @@ function makeGitUrls(opts: BinderOptions) {
* @param opts BinderOptions
* @returns a binder compatible url
*/
function makeGitLabUrl(opts: BinderOptions) {
if (!opts.repo) throw Error('repo is required for gitlab provider');
const binderUrl = opts.binderUrl?.replace(/(\/?$)/g, '');
function makeGitLabUrl(config: Config) {
if (!config.binder.repo) throw Error('repo is required for gitlab provider');
const binderUrl = config.binder.binderUrl?.replace(/(\/?$)/g, '');
const repo = encodeURIComponent(
(opts.repo ?? '').replace(/^(https?:\/\/)?gitlab.com\//, '').replace(/(^\/)|(\/?$)/g, ''),
(config.binder.repo ?? '')
.replace(/^(https?:\/\/)?gitlab.com\//, '')
.replace(/(^\/)|(\/?$)/g, ''),
);
const stub = `gl/${repo}/${opts.ref ?? 'HEAD'}`;
return {
build: `${binderUrl}/build/${stub}`,
launch: `${binderUrl}/v2/${stub}`,
};
const stub = `gl/${repo}/${config.binder.ref ?? 'HEAD'}`;
return makeDefaultBuildSpec(config.savedSessions.storagePrefix, binderUrl, stub);
}

/**
Expand All @@ -57,26 +66,26 @@ function makeGitLabUrl(opts: BinderOptions) {
* @param opts BinderOptions
* @returns a binder compatible url
*/
function makeGitHubUrl(opts: BinderOptions) {
if (!opts.repo) throw Error('repo is required for github provider');
const repo = opts.repo.replace(/^(https?:\/\/)?github.com\//, '').replace(/(^\/)|(\/?$)/g, '');
const binderUrl = opts.binderUrl?.replace(/(\/?$)/g, '');
const stub = `gh/${repo}/${opts.ref ?? 'HEAD'}`;
return {
build: `${binderUrl}/build/${stub}`,
launch: `${binderUrl}/v2/${stub}`,
};
function makeGitHubUrl(config: Config) {
if (!config.binder.repo) throw Error('repo is required for github provider');
const repo = config.binder.repo
.replace(/^(https?:\/\/)?github.com\//, '')
.replace(/(^\/)|(\/?$)/g, '');
const binderUrl = config.binder.binderUrl?.replace(/(\/?$)/g, '');
const stub = `gh/${repo}/${config.binder.ref ?? 'HEAD'}`;

return makeDefaultBuildSpec(config.savedSessions.storagePrefix, binderUrl, stub);
}

function makeGistUrl(opts: BinderOptions) {
if (!opts.repo) throw Error('repo is required for gist provider');
const repo = opts.repo.replace(/^(https?:\/\/)?github.com\//, '').replace(/(^\/)|(\/?$)/g, '');
const binderUrl = opts.binderUrl?.replace(/(\/?$)/g, '');
const stub = `gist/${repo}/${opts.ref ?? 'HEAD'}`;
return {
build: `${binderUrl}/build/${stub}`,
launch: `${binderUrl}/v2/${stub}`,
};
function makeGistUrl(config: Config) {
if (!config.binder.repo) throw Error('repo is required for gist provider');
const repo = config.binder.repo
.replace(/^(https?:\/\/)?github.com\//, '')
.replace(/(^\/)|(\/?$)/g, '');
const binderUrl = config.binder.binderUrl?.replace(/(\/?$)/g, '');
const stub = `gist/${repo}/${config.binder.ref ?? 'HEAD'}`;

return makeDefaultBuildSpec(config.savedSessions.storagePrefix, binderUrl, stub);
}

export const GITHUB_SPEC: RepoProviderSpec = {
Expand Down Expand Up @@ -107,18 +116,15 @@ export const WELL_KNOWN_REPO_PROVIDERS = [GITHUB_SPEC, GITLAB_SPEC, GIT_SPEC, GI
* Custom providers are supported by passing in an array of CustomRepoProviderSpecs.
*
*/
export function makeBinderUrls(
opts: BinderOptions,
repoProviders: RepoProviderSpec[],
): BinderUrlSet {
export function makeBinderUrls(config: Config, repoProviders: RepoProviderSpec[]): BinderUrlSet {
const providerMap: Record<string, RepoProviderSpec> =
repoProviders.reduce((obj, spec) => ({ ...obj, [spec.name]: spec }), {}) ?? {};

const provider = opts.repoProvider ?? 'github';
const provider = config.binder.repoProvider ?? 'github';
if (!Object.keys(providerMap).includes(provider))
throw Error(`Unknown provider ${opts.repoProvider}`);
throw Error(`Unknown provider ${config.binder.repoProvider}`);

if (!providerMap[provider].makeUrls) throw Error(`No makeUrls function for ${provider}`);

return providerMap[provider].makeUrls(opts);
return providerMap[provider].makeUrls(config);
}
2 changes: 1 addition & 1 deletion packages/core/tests/config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe('config', () => {
expect(config.savedSessions).toEqual({
enabled: true,
maxAge: 86400,
storagePrefix: 'thebe-binder-',
storagePrefix: 'thebe-binder',
});
});
test('server settings', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/tests/options.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ describe('options', () => {
expect(makeSavedSessionOptions({})).toEqual({
enabled: true,
maxAge: 86400,
storagePrefix: 'thebe-binder-',
storagePrefix: 'thebe-binder',
});
});
test('overrides', () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/core/tests/sessions.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, test, expect } from 'vitest';
import { makeStorageKey } from '../src/sessions';
import { makeDefaultStorageKey } from '../src/sessions';

describe('session saving', () => {
describe('make storage key', () => {
Expand All @@ -19,7 +19,7 @@ describe('session saving', () => {
['https://mybinder.org/', 'prefix-https://mybinder.org/'],
['https://mybinder.org:1234/', 'prefix-https://mybinder.org:1234/'],
])('%s', (url, result) => {
expect(makeStorageKey('prefix', url)).toEqual(result);
expect(makeDefaultStorageKey('prefix', url)).toEqual(result);
});
});
});
Loading

0 comments on commit 0759842

Please sign in to comment.