Skip to content

Commit

Permalink
Merge branch 'main' into amckinney/go/unknown
Browse files Browse the repository at this point in the history
  • Loading branch information
amckinney committed Sep 6, 2024
2 parents 864ae6f + 978b2eb commit b2b6358
Show file tree
Hide file tree
Showing 821 changed files with 38,784 additions and 16 deletions.
8 changes: 8 additions & 0 deletions packages/cli/cli/versions.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
- changelogEntry:
- summary: |
Fix an issue with non-deterministic file ordering when OpenAPI is used as input.
type: fix
createdAt: '2024-09-06'
irVersion: 53
version: 0.41.5

- changelogEntry:
- summary: |
The Fern OpenAPI importer now handles importing an array for the `type` key.
Expand Down

Large diffs are not rendered by default.

53 changes: 53 additions & 0 deletions packages/cli/openapi-ir-to-fern/src/FernDefinitionDirectory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { RelativeFilePath } from "@fern-api/fs-utils";
import { RawSchemas } from "@fern-api/fern-definition-schema";
import path from "path";

export class FernDefinitionDirectory {
private files: Record<RelativeFilePath, RawSchemas.DefinitionFileSchema> = {};
private directories: Record<string, FernDefinitionDirectory> = {};

public getAllFiles(): Record<RelativeFilePath, RawSchemas.DefinitionFileSchema> {
const files: Record<RelativeFilePath, RawSchemas.DefinitionFileSchema> = {};

const walk = (root: FernDefinitionDirectory, currentPath?: string) => {
for (const [relativeFilePath, definition] of Object.entries(root.files)) {
const fullRelativeFilePath =
currentPath != null
? RelativeFilePath.of(`${currentPath}${path.sep}${relativeFilePath}`)
: RelativeFilePath.of(relativeFilePath);
files[fullRelativeFilePath] = definition;
}
const sortedDirectories = Object.keys(root.directories).sort();
for (const directory of sortedDirectories) {
const nextPath = currentPath != null ? `${currentPath}${path.sep}${directory}` : directory;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const nextDirectory = root.directories[directory]!;
walk(nextDirectory, nextPath);
}
};

walk(this);

return files;
}

public getOrCreateFile(relativeFilePath: RelativeFilePath): RawSchemas.DefinitionFileSchema {
return this.getOrCreateFileRecursive(relativeFilePath.split(path.sep));
}

private getOrCreateFileRecursive(pathParts: string[]): RawSchemas.DefinitionFileSchema {
if (pathParts.length === 1) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return (this.files[RelativeFilePath.of(pathParts[0]!)] ??= {});
}
const [directory, ...remainingPath] = pathParts;
if (directory == null) {
throw new Error(`Internal error; cannot add file with path: ${pathParts}`);
}
if (!this.directories[directory]) {
this.directories[directory] = new FernDefinitionDirectory();
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return this.directories[directory]!.getOrCreateFileRecursive(remainingPath);
}
}
13 changes: 8 additions & 5 deletions packages/cli/openapi-ir-to-fern/src/FernDefnitionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { RawSchemas, RootApiFileSchema, visitRawEnvironmentDeclaration } from "@
import { camelCase, isEqual } from "lodash-es";
import path, { basename, extname } from "path";
import { convertToSourceSchema } from "./utils/convertToSourceSchema";
import { FernDefinitionDirectory } from "./FernDefinitionDirectory";

export interface FernDefinitionBuilder {
addNavigation({ navigation }: { navigation: string[] }): void;
Expand Down Expand Up @@ -86,16 +87,17 @@ export interface FernDefinition {
}

export class FernDefinitionBuilderImpl implements FernDefinitionBuilder {
private root: FernDefinitionDirectory;
private rootApiFile: RawSchemas.RootApiFileSchema;
private packageMarkerFile: RawSchemas.PackageMarkerFileSchema = {};
private definitionFiles: Record<RelativeFilePath, RawSchemas.DefinitionFileSchema> = {};
private basePath: string | undefined = undefined;

public constructor(
ir: OpenApiIntermediateRepresentation,
private readonly modifyBasePaths: boolean,
public readonly enableUniqueErrorsPerEndpoint: boolean
) {
this.root = new FernDefinitionDirectory();
this.rootApiFile = {
name: "api",
"error-discrimination": {
Expand Down Expand Up @@ -404,6 +406,7 @@ export class FernDefinitionBuilderImpl implements FernDefinitionBuilder {
}

public build(): FernDefinition {
const definitionFiles = this.root.getAllFiles();
if (this.modifyBasePaths) {
const basePath = getSharedEnvironmentBasePath(this.rootApiFile);

Expand All @@ -426,7 +429,7 @@ export class FernDefinitionBuilderImpl implements FernDefinitionBuilder {
}

// subsitute definition files
for (const [_, file] of Object.entries(this.definitionFiles)) {
for (const file of Object.values(definitionFiles)) {
if (file.service != null) {
file.service = {
...file.service,
Expand Down Expand Up @@ -497,7 +500,7 @@ export class FernDefinitionBuilderImpl implements FernDefinitionBuilder {
}

// subsitute definition files
for (const [_, file] of Object.entries(this.definitionFiles)) {
for (const file of Object.values(definitionFiles)) {
if (file.service != null) {
file.service = {
...file.service,
Expand All @@ -520,7 +523,7 @@ export class FernDefinitionBuilderImpl implements FernDefinitionBuilder {
const definition: FernDefinition = {
rootApiFile: this.rootApiFile,
packageMarkerFile: this.packageMarkerFile,
definitionFiles: this.definitionFiles
definitionFiles
};
return definition;
}
Expand All @@ -531,7 +534,7 @@ export class FernDefinitionBuilderImpl implements FernDefinitionBuilder {
if (file === FERN_PACKAGE_MARKER_FILENAME) {
return this.packageMarkerFile;
} else {
return (this.definitionFiles[file] ??= {});
return this.root.getOrCreateFile(file);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { RawSchemas } from "@fern-api/fern-definition-schema";
import { RelativeFilePath } from "@fern-api/fs-utils";
import { FernDefinitionDirectory } from "../FernDefinitionDirectory";

interface TestCase {
description: string;
giveFilepaths: string[];
wantFiles: Record<string, RawSchemas.DefinitionFileSchema>;
}

describe("FernDefinitionDirectory", () => {
const testCases: TestCase[] = [
{
description: "empty",
giveFilepaths: [],
wantFiles: {}
},
{
description: "single file",
giveFilepaths: ["example.yml"],
wantFiles: {
"example.yml": {}
}
},
{
description: "single directory",
giveFilepaths: ["one/a.yml"],
wantFiles: {
"one/a.yml": {}
}
},
{
description: "single directory, multiple files",
giveFilepaths: ["one/b.yml", "one/a.yml"],
wantFiles: {
"one/a.yml": {},
"one/b.yml": {}
}
},
{
description: "multiple directory, multiple files",
giveFilepaths: ["one/b.yml", "two/foo/d.yml", "two/foo/c.yml", "one/a.yml"],
wantFiles: {
"one/a.yml": {},
"one/b.yml": {},
"two/foo/c.yml": {},
"two/foo/d.yml": {}
}
},
{
description: "file/directory match",
giveFilepaths: ["user/events/metadata.yml", "user/events.yml", "user.yml", "events.yml"],
wantFiles: {
"events.yml": {},
"user.yml": {},
"user/events.yml": {},
"user/events/metadata.yml": {}
}
}
];

testCases.forEach((testCase) => {
it(`"${testCase.description}"`, async () => {
const root = new FernDefinitionDirectory();
for (const filepath of testCase.giveFilepaths) {
root.getOrCreateFile(RelativeFilePath.of(filepath));
}
expect(root.getAllFiles()).toEqual(testCase.wantFiles);
});
});
});
Loading

0 comments on commit b2b6358

Please sign in to comment.