Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ type RevokeRefreshTokenRequestBody = {
/** Path on extension redirect URL for picking up auth response */
export const AUTH_PATH = '/callback/auth0';

/** Error message thrown when not logged in when trying to get access token */
export const NOT_LOGGED_IN_ERROR_MESSAGE = 'Not logged in';

/** Necessary auth scopes for accessing Slingshot drafts */
const SCOPES = ['openid', 'profile', 'email', 'sf_data', 'offline_access'].join(' ');
/** Auth audience for Scripture Forge...? */
Expand Down Expand Up @@ -492,7 +495,7 @@ export default class ScriptureForgeAuthenticationProvider implements Dispose {
}
this.#hasRetrievedAuthTokensFromStorage = true;
}
if (!this.#authTokens) throw new Error('Not logged in');
if (!this.#authTokens) throw new Error(NOT_LOGGED_IN_ERROR_MESSAGE);

if (!this.#authTokens.didExpire && this.#authTokens.accessTokenExpireTime > Date.now())
return this.#authTokens.accessToken;
Expand Down
8 changes: 7 additions & 1 deletion src/scripture-forge/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,13 @@ export async function activate(context: ExecutionActivationContext) {
// const scriptureForgeApi = new ScriptureForgeSampleApi(authenticationProvider);
const scriptureForgeApi = new ScriptureForgeApi(authenticationProvider);

const slingshotPdpef = new SlingshotProjectDataProviderEngineFactory(scriptureForgeApi);
const slingshotPdpef = new SlingshotProjectDataProviderEngineFactory(
scriptureForgeApi,
sessionChangeEmitter.event,
);

context.registrations.add(slingshotPdpef);

const slingshotPdpefPromise = papi.projectDataProviders.registerProjectDataProviderEngineFactory(
SCRIPTURE_FORGE_SLINGSHOT_PDPF_ID,
SLINGSHOT_PROJECT_INTERFACES,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import {
ProjectMetadataWithoutFactoryInfo,
} from '@papi/core';
import { logger } from '@papi/backend';
import { getErrorMessage, Mutex } from 'platform-bible-utils';
import { Dispose, getErrorMessage, Mutex, PlatformEvent, Unsubscriber } from 'platform-bible-utils';
import SlingshotProjectDataProviderEngine, {
SLINGSHOT_PROJECT_INTERFACES,
} from './slingshot-project-data-provider-engine.model';
import ScriptureForgeApi, { ScriptureForgeProjectInfo } from './scripture-forge-api.model';
import { NOT_LOGGED_IN_ERROR_MESSAGE } from '../auth/scripture-forge-authentication-provider.model';

/**
* Duration in milliseconds to throttle the `getProjects` API call. We will return the previous
Expand All @@ -30,16 +31,29 @@ function getSlingshotAppProjectId(projectId: string): string {
}

export default class SlingshotProjectDataProviderEngineFactory
implements IProjectDataProviderEngineFactory<typeof SLINGSHOT_PROJECT_INTERFACES>
implements IProjectDataProviderEngineFactory<typeof SLINGSHOT_PROJECT_INTERFACES>, Dispose
{
private projectInfoByAppProjectId = new Map<string, ScriptureForgeProjectInfo>();
private pdpeByAppProjectId = new Map<string, SlingshotProjectDataProviderEngine>();
/** Last time we ran `getProjects` on the API so we can throttle it */
private lastGetProjectsTime: number = 0;
private lastAvailableProjects: ProjectMetadataWithoutFactoryInfo[] = [];
private getProjectsMutex = new Mutex();

constructor(private scriptureForgeApi: ScriptureForgeApi) {}
/**
* If we know we are logged out and therefore shouldn't try to check for projects. If this is
* `false`, it doesn't mean we are logged in but that we need to try getting projects again
*/
private isLoggedOut = false;
private onDidSessionChangeUnsubscriber: Unsubscriber;

constructor(
private scriptureForgeApi: ScriptureForgeApi,
onDidSessionChange: PlatformEvent<undefined>,
) {
this.onDidSessionChangeUnsubscriber = onDidSessionChange(() => {
this.isLoggedOut = false;
});
}

async getAvailableProjects(): Promise<ProjectMetadataWithoutFactoryInfo[]> {
return this.#getAvailableProjects();
Expand All @@ -64,13 +78,20 @@ export default class SlingshotProjectDataProviderEngineFactory
return pdpe;
}

async dispose(): Promise<boolean> {
return this.onDidSessionChangeUnsubscriber();
}

/**
* Gets available projects from cache or reaches out to get new project info
*
* @param force If `true`, will get new project info
*/
async #getAvailableProjects(force = false) {
if (force) this.lastGetProjectsTime = 0;
// If we're not forcing and we know we're logged out, return empty array instead of failing
// again because we know we're already logged out
else if (this.isLoggedOut) return [];

if (Date.now() - this.lastGetProjectsTime < GET_PROJECTS_THROTTLE_MS)
return this.lastAvailableProjects;
Expand All @@ -86,9 +107,14 @@ export default class SlingshotProjectDataProviderEngineFactory
try {
projectsInfo = await this.scriptureForgeApi.getProjects();
} catch (e) {
const errorMessage = getErrorMessage(e);
logger.warn(
`Slingshot PDPEF caught Scripture Forge API error while getting available projects. ${getErrorMessage(e)}`,
`Slingshot PDPEF caught Scripture Forge API error while getting available projects. ${errorMessage}`,
);

// Keep track if we're logged out so we don't keep spamming the logs
if (errorMessage === NOT_LOGGED_IN_ERROR_MESSAGE) this.isLoggedOut = true;

return [];
}

Expand Down