Skip to content

Commit

Permalink
fix: make sure endpoints are cached in nextjs route on register
Browse files Browse the repository at this point in the history
  • Loading branch information
dsinghvi committed Oct 11, 2024
1 parent 6478b57 commit 747181f
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 34 deletions.
82 changes: 53 additions & 29 deletions servers/fdr/src/controllers/docs/v2/getDocsWriteV2Service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { APIV1Db, convertDocsDefinitionToDb, DocsV1Db, DocsV1Write, FdrAPI } from "@fern-api/fdr-sdk";
import { isNonNullish } from "@fern-api/ui-core-utils";
import { AuthType } from "@prisma/client";
import urlJoin from "url-join";
import { v4 as uuidv4 } from "uuid";
Expand Down Expand Up @@ -147,14 +148,24 @@ export function getDocsWriteV2Service(app: FdrApplication): DocsV2WriteService {
files: docsRegistrationInfo.s3FileInfos,
});

const apiDefinitionsById = await (async () => {
const apiIdDefinitionTuples = await Promise.all(
dbDocsDefinition.referencedApis.map(
async (id) => [id, await app.services.db.getApiDefinition(id)] as const,
),
);
return new Map(apiIdDefinitionTuples) as Map<string, APIV1Db.DbApiDefinition>;
})();
const apiDefinitions = (
await Promise.all(
dbDocsDefinition.referencedApis.map(async (id) => await app.services.db.getApiDefinition(id)),
)
).filter(isNonNullish);
const apiDefinitionsById = Object.fromEntries(
apiDefinitions.map((definition) => [definition.id, definition]),
);

const warmEndpointCachePromises = apiDefinitions.flatMap((apiDefinition) => {
return Object.entries(apiDefinition.subpackages).flatMap(([id, subpackage]) => {
return subpackage.endpoints.map(async (endpoint) => {
return await fetch(
`${docsRegistrationInfo.fernUrl.getFullUrl()}/api/fern-docs/api-definition/${apiDefinition.id}/endpoint/${endpoint.id}`,
);
});
});
});

const indexSegments = await uploadToAlgoliaForRegistration(
app,
Expand All @@ -175,19 +186,30 @@ export function getDocsWriteV2Service(app: FdrApplication): DocsV2WriteService {
*/
const urls = [docsRegistrationInfo.fernUrl, ...docsRegistrationInfo.customUrls];

await Promise.all(
urls.map(async (baseUrl) => {
const results = await app.services.revalidator.revalidate({ baseUrl, app });
if (results.failed.length === 0 && !results.revalidationFailed) {
app.logger.info(`Successfully revalidated ${results.successful.length} paths.`);
} else {
await app.services.slack.notifyFailedToRevalidatePaths({
domain: baseUrl.getFullUrl(),
paths: results,
});
}
}),
);
try {
await Promise.all(
urls.map(async (baseUrl) => {
const results = await app.services.revalidator.revalidate({ baseUrl, app });
if (results.failed.length === 0 && !results.revalidationFailed) {
app.logger.info(`Successfully revalidated ${results.successful.length} paths.`);
} else {
await app.services.slack.notifyFailedToRevalidatePaths({
domain: baseUrl.getFullUrl(),
paths: results,
});
}
}),
);
} catch (e) {
app.logger.error(`Error while trying to revalidate docs for ${docsRegistrationInfo.fernUrl}`, e);
await app.services.slack.notifyFailedToRegisterDocs({
domain: docsRegistrationInfo.fernUrl.getFullUrl(),
err: e,
});
throw e;
}

await Promise.all(warmEndpointCachePromises);

return res.send();
} catch (e) {
Expand All @@ -212,14 +234,16 @@ export function getDocsWriteV2Service(app: FdrApplication): DocsV2WriteService {
throw new ReindexNotAllowedError();
}

const apiDefinitionsById = await (async () => {
const apiIdDefinitionTuples = await Promise.all(
const apiDefinitions = (
await Promise.all(
response.docsDefinition.referencedApis.map(
async (id) => [id, await app.services.db.getApiDefinition(id)] as const,
async (id) => await app.services.db.getApiDefinition(id),
),
);
return new Map(apiIdDefinitionTuples) as Map<string, APIV1Db.DbApiDefinition>;
})();
)
).filter(isNonNullish);
const apiDefinitionsById = Object.fromEntries(
apiDefinitions.map((definition) => [definition.id, definition]),
);

// step 2. create new index segments in algolia
const indexSegments = await uploadToAlgolia(
Expand All @@ -245,7 +269,7 @@ async function uploadToAlgoliaForRegistration(
app: FdrApplication,
docsRegistrationInfo: DocsRegistrationInfo,
dbDocsDefinition: WithoutQuestionMarks<DocsV1Db.DocsDefinitionDb>,
apiDefinitionsById: Map<string, APIV1Db.DbApiDefinition>,
apiDefinitionsById: Record<string, APIV1Db.DbApiDefinition>,
): Promise<IndexSegment[]> {
// TODO: make sure to store private docs index into user-restricted algolia index
// see https://www.algolia.com/doc/guides/security/api-keys/how-to/user-restricted-access-to-data/
Expand All @@ -265,7 +289,7 @@ async function uploadToAlgolia(
app: FdrApplication,
url: ParsedBaseUrl,
dbDocsDefinition: WithoutQuestionMarks<DocsV1Db.DocsDefinitionDb>,
apiDefinitionsById: Map<string, APIV1Db.DbApiDefinition>,
apiDefinitionsById: Record<string, APIV1Db.DbApiDefinition>,
): Promise<IndexSegment[]> {
app.logger.debug(`[${url.getFullUrl()}] Generating new index segments`);
const generateNewIndexSegmentsResult = app.services.algoliaIndexSegmentManager.generateIndexSegmentsForDefinition({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import type { AlgoliaSearchRecord, IndexSegment } from "./types";

interface AlgoliaSearchRecordGeneratorConfig {
docsDefinition: DocsV1Db.DocsDefinitionDb;
apiDefinitionsById: Map<string, APIV1Db.DbApiDefinition>;
apiDefinitionsById: Record<string, APIV1Db.DbApiDefinition>;
}

export class AlgoliaSearchRecordGenerator {
Expand Down Expand Up @@ -140,7 +140,7 @@ export class AlgoliaSearchRecordGenerator {
const records: AlgoliaSearchRecord[] = [];
const api = item;
const apiId = api.api;
const apiDef = this.config.apiDefinitionsById.get(apiId);
const apiDef = this.config.apiDefinitionsById[apiId];
if (apiDef != null) {
records.push(
...this.generateAlgoliaSearchRecordsForApiDefinition(
Expand Down Expand Up @@ -232,7 +232,7 @@ export class AlgoliaSearchRecordGenerator {
root: FernNavigation.V1.ApiReferenceNode,
context: NavigationContext,
): AlgoliaSearchRecord[] {
const api = this.config.apiDefinitionsById.get(root.apiDefinitionId);
const api = this.config.apiDefinitionsById[root.apiDefinitionId];
if (api == null) {
LOGGER.error("Failed to find API definition for API reference node. id=", root.apiDefinitionId);
}
Expand Down
4 changes: 2 additions & 2 deletions servers/fdr/src/services/algolia/AlgoliaService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export interface AlgoliaService {
generateSearchRecords(params: {
url: string;
docsDefinition: DocsV1Db.DocsDefinitionDb;
apiDefinitionsById: Map<string, APIV1Db.DbApiDefinition>;
apiDefinitionsById: Record<string, APIV1Db.DbApiDefinition>;
configSegmentTuples: ConfigSegmentTuple[];
}): Promise<AlgoliaSearchRecord[]>;

Expand Down Expand Up @@ -48,7 +48,7 @@ export class AlgoliaServiceImpl implements AlgoliaService {
}: {
url: string;
docsDefinition: DocsV1Db.DocsDefinitionDb;
apiDefinitionsById: Map<string, APIV1Db.DbApiDefinition>;
apiDefinitionsById: Record<string, APIV1Db.DbApiDefinition>;
configSegmentTuples: ConfigSegmentTuple[];
}) {
return configSegmentTuples.flatMap(([config, indexSegment]) => {
Expand Down

0 comments on commit 747181f

Please sign in to comment.