Skip to content

Commit

Permalink
fix(cli): consume the origin from the v2 config (#4834)
Browse files Browse the repository at this point in the history
  • Loading branch information
armandobelardo authored Oct 7, 2024
1 parent 531c15a commit df619ef
Show file tree
Hide file tree
Showing 8 changed files with 4,815 additions and 75 deletions.
150 changes: 76 additions & 74 deletions packages/cli/cli/src/commands/upgrade/updateApiSpec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { generatorsYml, getFernDirectory } from "@fern-api/configuration";
import { isPlainObject } from "@fern-api/core-utils";
import { join, RelativeFilePath } from "@fern-api/fs-utils";
import { assertNever, isPlainObject } from "@fern-api/core-utils";
import { join, RelativeFilePath, AbsoluteFilePath } from "@fern-api/fs-utils";
import { Logger } from "@fern-api/logger";
import { Project } from "@fern-api/project-loader";
import * as fs from "fs";
Expand Down Expand Up @@ -44,88 +44,90 @@ export async function updateApiSpec({
}

await cliContext.runTaskForWorkspace(workspace, async (context) => {
const generatorConfig =
(await generatorsYml.loadRawGeneratorsConfiguration({
const generatorConfig: generatorsYml.GeneratorsConfiguration | undefined =
await generatorsYml.loadGeneratorsConfiguration({
absolutePathToWorkspace: workspace.absoluteFilePath,
context
})) ?? {};

const fernDirectory = await getFernDirectory();
if (fernDirectory == null) {
return context.failAndThrow("Fern directory not found.");
});
if (generatorConfig == null) {
cliContext.logger.info("No API configuration was found, skipping API update.");
return;
}

if (generatorConfig.api != null) {
let apis;
if (generatorConfig.api instanceof Array) {
apis = generatorConfig.api;
} else {
apis = [generatorConfig.api];
}
for (const api of apis) {
if (generatorsYml.isRawProtobufAPIDefinitionSchema(api)) {
continue;
}
if (isPlainObject(api) && "origin" in api && api.origin != null) {
cliContext.logger.info(`Origin found, fetching spec from ${api.origin}`);
await fetchAndWriteFile(
api.origin,
join(workspace.absoluteFilePath, RelativeFilePath.of(api.path)),
cliContext.logger
);
} else if (isPlainObject(api)) {
for (const [_, value] of Object.entries(api)) {
if (
isPlainObject(value) &&
"origin" in value &&
typeof value.origin === "string" &&
"path" in value &&
typeof value.path === "string"
) {
cliContext.logger.info(`Origin found, fetching spec from ${value.origin}`);
await fetchAndWriteFile(
value.origin,
join(workspace.absoluteFilePath, RelativeFilePath.of(value.path)),
cliContext.logger
);
}
}
}
if (generatorConfig.api.type === "conjure") {
cliContext.logger.info("Encountered conjure API definition, skipping API update.");
return;
}
} else if (generatorConfig[generatorsYml.ASYNC_API_LOCATION_KEY] != null) {
if (generatorConfig[generatorsYml.API_ORIGIN_LOCATION_KEY] != null) {
cliContext.logger.info(
`Origin found, fetching spec from ${generatorConfig[generatorsYml.API_ORIGIN_LOCATION_KEY]}`
);
const origin = generatorConfig[generatorsYml.API_ORIGIN_LOCATION_KEY];
const location = generatorConfig[generatorsYml.ASYNC_API_LOCATION_KEY];
if (origin != null && location != null) {
await fetchAndWriteFile(
origin,
join(workspace.absoluteFilePath, RelativeFilePath.of(location)),
cliContext.logger
);
if (generatorConfig.api.type === "singleNamespace") {
await processDefinitions({
cliContext,
workspacePath: workspace.absoluteFilePath,
apiLocations: generatorConfig.api.definitions
});
return;
} else if (generatorConfig.api.type === "multiNamespace") {
// process root definitions
if (generatorConfig.api.rootDefinitions != null) {
await processDefinitions({
cliContext,
workspacePath: workspace.absoluteFilePath,
apiLocations: generatorConfig.api.rootDefinitions
});
}
}
} else if (generatorConfig[generatorsYml.OPENAPI_LOCATION_KEY] != null) {
const apiBlock = generatorConfig[generatorsYml.OPENAPI_LOCATION_KEY];
const apiOrigin =
typeof apiBlock !== "string"
? apiBlock?.origin ?? generatorConfig[generatorsYml.API_ORIGIN_LOCATION_KEY]
: generatorConfig[generatorsYml.API_ORIGIN_LOCATION_KEY];

const apiOutput = typeof apiBlock !== "string" ? apiBlock?.path : apiBlock;

if (apiOrigin != null && apiOutput != null) {
origin = apiOrigin;
cliContext.logger.info(`Origin found, fetching spec from ${apiOrigin}`);
await fetchAndWriteFile(
apiOrigin,
join(workspace.absoluteFilePath, RelativeFilePath.of(apiOutput)),
cliContext.logger
);
// process namespaced definitions
for (const [_, apiLocations] of Object.entries(generatorConfig.api.definitions)) {
await processDefinitions({
cliContext,
workspacePath: workspace.absoluteFilePath,
apiLocations
});
}
}
}
return;
});
}
}

async function getAndFetchFromAPIDefinitionLocation({
cliContext,
workspacePath,
apiLocation
}: {
cliContext: CliContext;
workspacePath: AbsoluteFilePath;
apiLocation: generatorsYml.APIDefinitionLocation;
}) {
if (apiLocation.schema.type === "protobuf") {
cliContext.logger.info("Encountered conjure API definition, skipping API update.");
return;
}
if (apiLocation.origin != null) {
cliContext.logger.info(`Origin found, fetching spec from ${apiLocation.origin}`);
await fetchAndWriteFile(
apiLocation.origin,
join(workspacePath, RelativeFilePath.of(apiLocation.schema.path)),
cliContext.logger
);
}
}

async function processDefinitions({
cliContext,
workspacePath,
apiLocations
}: {
cliContext: CliContext;
workspacePath: AbsoluteFilePath;
apiLocations: generatorsYml.APIDefinitionLocation[];
}) {
for (const apiLocation of apiLocations) {
await getAndFetchFromAPIDefinitionLocation({
cliContext,
workspacePath,
apiLocation
});
}
}
9 changes: 8 additions & 1 deletion packages/cli/cli/versions.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
- changelogEntry:
- summary: |
API update now supports consuming the API origin from spec V2 configurations.
type: fix
irVersion: 53
version: 0.44.10

- changelogEntry:
- summary: |
The fern definition now supports descriptions supplied on request and response bodies.
You can enable this by simply supplying `docs` in your fern definition, or `description`
You can enable this by simply supplying `docs` in your fern definition, or `description`
in your OpenAPI spec.
type: feat
irVersion: 53
Expand Down
Loading

0 comments on commit df619ef

Please sign in to comment.