Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wip: add a getDefinition function to a fernworkspace #3784

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -39,8 +39,9 @@ export async function registerWorkspacesV1({
context.failWithoutThrowing("Registering from OpenAPI not currently supported.");
return;
}
const workspaceDefinition = await workspace.getDefinition();
const registerApiResponse = await fiddle.definitionRegistry.registerUsingOrgToken({
apiId: FernFiddle.ApiId(workspace.definition.rootApiFile.contents.name),
apiId: FernFiddle.ApiId(workspaceDefinition.rootApiFile.contents.name),
version,
cliVersion: cliContext.environment.packageVersion,
yamlSchemaVersion: `${YAML_SCHEMA_VERSION}`
Expand All @@ -67,7 +68,7 @@ export async function registerWorkspacesV1({
await axios.put(registerApiResponse.body.definitionS3UploadUrl, await readFile(tarPath));

context.logger.info(
`Registered @${project.config.organization}/${workspace.definition.rootApiFile.contents.name}:${registerApiResponse.body.version}`
`Registered @${project.config.organization}/${workspaceDefinition.rootApiFile.contents.name}:${registerApiResponse.body.version}`
);
});
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ async function writeDefinitionForFernWorkspace({
workspace: FernWorkspace;
context: TaskContext;
}): Promise<void> {
for (const [relativePath, definition] of Object.entries(workspace.definition.importedDefinitions)) {
const workspaceDefinition = await workspace.getDefinition();
for (const [relativePath, definition] of Object.entries(workspaceDefinition.importedDefinitions)) {
const absolutePathToOutputDirectory = join(
workspace.absoluteFilepath,
RelativeFilePath.of(DEFINITION_DIRECTORY),
Expand Down Expand Up @@ -75,7 +76,7 @@ async function writeDefinitionForOpenAPIWorkspace({
RelativeFilePath.of(`.${DEFINITION_DIRECTORY}`)
);
await writeFernDefinition({
definition: fernWorkspace.definition,
definition: await fernWorkspace.getDefinition(),
absolutePathToOutputDirectory
});
context.logger.info(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export function validateObjectExample({
relativeFilepath: propertyWithPath.filepathOfDeclaration,
definitionFile,
casingsGenerator: file.casingsGenerator,
rootApiFile: workspace.definition.rootApiFile.contents
rootApiFile: (await workspace.getDefinition()).rootApiFile.contents
}),
workspace,
typeResolver,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,15 @@ export async function generateIntermediateRepresentation({
audiences: Audiences;
}): Promise<IntermediateRepresentation> {
const casingsGenerator = constructCasingsGenerator({ generationLanguage, keywords, smartCasing });
const workspaceDefinition = await workspace.getDefinition(generationLanguage);

const irGraph = new IrGraph(audiences);

const rootApiFileContext = constructRootApiFileContext({
casingsGenerator,
rootApiFile: workspace.definition.rootApiFile.contents
rootApiFile: workspaceDefinition.rootApiFile.contents
});
const globalErrors: ResponseErrors = (workspace.definition.rootApiFile.contents.errors ?? []).map(
const globalErrors: ResponseErrors = (workspaceDefinition.rootApiFile.contents.errors ?? []).map(
(referenceToError) => {
const errorName = parseErrorName({
errorName: referenceToError,
Expand All @@ -85,26 +86,26 @@ export async function generateIntermediateRepresentation({

const intermediateRepresentation: Omit<IntermediateRepresentation, "sdkConfig" | "subpackages" | "rootPackage"> = {
apiName: casingsGenerator.generateName(workspace.name),
apiDisplayName: workspace.definition.rootApiFile.contents["display-name"],
apiDocs: await formatDocs(workspace.definition.rootApiFile.contents.docs),
apiDisplayName: workspaceDefinition.rootApiFile.contents["display-name"],
apiDocs: await formatDocs(workspaceDefinition.rootApiFile.contents.docs),
auth: await convertApiAuth({
rawApiFileSchema: workspace.definition.rootApiFile.contents,
rawApiFileSchema: workspaceDefinition.rootApiFile.contents,
file: rootApiFileContext,
propertyResolver,
endpointResolver
}),
headers:
workspace.definition.rootApiFile.contents.headers != null
workspaceDefinition.rootApiFile.contents.headers != null
? await Promise.all(
Object.entries(workspace.definition.rootApiFile.contents.headers).map(([headerKey, header]) =>
Object.entries(workspaceDefinition.rootApiFile.contents.headers).map(([headerKey, header]) =>
convertHttpHeader({ headerKey, header, file: rootApiFileContext })
)
)
: [],
idempotencyHeaders:
workspace.definition.rootApiFile.contents["idempotency-headers"] != null
workspaceDefinition.rootApiFile.contents["idempotency-headers"] != null
? await Promise.all(
Object.entries(workspace.definition.rootApiFile.contents["idempotency-headers"]).map(
Object.entries(workspaceDefinition.rootApiFile.contents["idempotency-headers"]).map(
([headerKey, header]) => convertHttpHeader({ headerKey, header, file: rootApiFileContext })
)
)
Expand All @@ -115,25 +116,25 @@ export async function generateIntermediateRepresentation({
constants: generateFernConstants(casingsGenerator),
environments: convertEnvironments({
casingsGenerator,
rawApiFileSchema: workspace.definition.rootApiFile.contents
rawApiFileSchema: workspaceDefinition.rootApiFile.contents
}),
errorDiscriminationStrategy: convertErrorDiscriminationStrategy(
workspace.definition.rootApiFile.contents["error-discrimination"],
workspaceDefinition.rootApiFile.contents["error-discrimination"],
rootApiFileContext
),
basePath:
workspace.definition.rootApiFile.contents["base-path"] != null
? constructHttpPath(workspace.definition.rootApiFile.contents["base-path"])
workspaceDefinition.rootApiFile.contents["base-path"] != null
? constructHttpPath(workspaceDefinition.rootApiFile.contents["base-path"])
: undefined,
pathParameters: await convertPathParameters({
pathParameters: workspace.definition.rootApiFile.contents["path-parameters"],
pathParameters: workspaceDefinition.rootApiFile.contents["path-parameters"],
file: rootApiFileContext,
location: PathParameterLocation.Root,
variableResolver
}),
variables:
workspace.definition.rootApiFile.contents.variables != null
? Object.entries(workspace.definition.rootApiFile.contents.variables).map(([key, variable]) => ({
workspaceDefinition.rootApiFile.contents.variables != null
? Object.entries(workspaceDefinition.rootApiFile.contents.variables).map(([key, variable]) => ({
docs: typeof variable !== "string" ? variable.docs : undefined,
id: key,
name: rootApiFileContext.casingsGenerator.generateName(key),
Expand Down Expand Up @@ -200,7 +201,7 @@ export async function generateIntermediateRepresentation({
if (errors == null) {
return;
}
const errorDiscriminationSchema = workspace.definition.rootApiFile.contents["error-discrimination"];
const errorDiscriminationSchema = workspaceDefinition.rootApiFile.contents["error-discrimination"];
if (errorDiscriminationSchema == null) {
throw new Error("error-discrimination is missing in api.yml but there are declared errors.");
}
Expand Down Expand Up @@ -307,7 +308,7 @@ export async function generateIntermediateRepresentation({
relativeFilepath,
definitionFile: file,
casingsGenerator,
rootApiFile: workspace.definition.rootApiFile.contents
rootApiFile: workspaceDefinition.rootApiFile.contents
})
);
});
Expand Down Expand Up @@ -364,7 +365,7 @@ export async function generateIntermediateRepresentation({
);

const isAuthMandatory =
workspace.definition.rootApiFile.contents.auth != null &&
workspaceDefinition.rootApiFile.contents.auth != null &&
Object.values(intermediateRepresentationForAudiences.services).every((service) => {
return service.endpoints.every((endpoint) => endpoint.auth);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,31 @@ export interface ErrorResolver {
getDeclarationOrThrow(
referenceToError: string,
file: FernFileContext
): { declaration: RawSchemas.ErrorDeclarationSchema; file: FernFileContext };
): Promise<{ declaration: RawSchemas.ErrorDeclarationSchema; file: FernFileContext }>;
getDeclaration(
referenceToError: string,
file: FernFileContext
): { declaration: RawSchemas.ErrorDeclarationSchema; file: FernFileContext } | undefined;
): Promise<{ declaration: RawSchemas.ErrorDeclarationSchema; file: FernFileContext } | undefined>;
}

export class ErrorResolverImpl implements ErrorResolver {
constructor(private readonly workspace: FernWorkspace) {}

public getDeclarationOrThrow(
public async getDeclarationOrThrow(
referenceToError: string,
file: FernFileContext
): { declaration: RawSchemas.ErrorDeclarationSchema; file: FernFileContext } {
const declaration = this.getDeclaration(referenceToError, file);
): Promise<{ declaration: RawSchemas.ErrorDeclarationSchema; file: FernFileContext }> {
const declaration = await this.getDeclaration(referenceToError, file);
if (declaration == null) {
throw new Error("Error does not exist: " + referenceToError);
}
return declaration;
}

public getDeclaration(
public async getDeclaration(
referenceToError: string,
file: FernFileContext
): { declaration: RawSchemas.ErrorDeclarationSchema; file: FernFileContext } | undefined {
): Promise<{ declaration: RawSchemas.ErrorDeclarationSchema; file: FernFileContext } | undefined> {
const parsedReference = parseReferenceToTypeName({
reference: referenceToError,
referencedIn: file.relativeFilepath,
Expand All @@ -58,7 +58,7 @@ export class ErrorResolverImpl implements ErrorResolver {
definitionFile,
relativeFilepath: parsedReference.relativeFilepath,
casingsGenerator: file.casingsGenerator,
rootApiFile: this.workspace.definition.rootApiFile.contents
rootApiFile: (await this.workspace.getDefinition()).rootApiFile.contents
})
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ export interface TypeResolver {
getDeclarationOfNamedType: (args: {
referenceToNamedType: string;
file: FernFileContext;
}) => RawTypeDeclarationInfo | undefined;
}) => Promise<RawTypeDeclarationInfo | undefined>;
getDeclarationOfNamedTypeOrThrow: (args: {
referenceToNamedType: string;
file: FernFileContext;
}) => RawTypeDeclarationInfo;
}) => Promise<RawTypeDeclarationInfo>;
resolveNamedType: (args: { referenceToNamedType: string; file: FernFileContext }) => ResolvedType | undefined;
resolveNamedTypeOrThrow: (args: { referenceToNamedType: string; file: FernFileContext }) => ResolvedType;
}
Expand All @@ -38,14 +38,14 @@ export class TypeResolverImpl implements TypeResolver {
return resolvedType;
}

public getDeclarationOfNamedTypeOrThrow({
public async getDeclarationOfNamedTypeOrThrow({
referenceToNamedType,
file
}: {
referenceToNamedType: string;
file: FernFileContext;
}): RawTypeDeclarationInfo {
const declaration = this.getDeclarationOfNamedType({ referenceToNamedType, file });
}): Promise<RawTypeDeclarationInfo> {
const declaration = await this.getDeclarationOfNamedType({ referenceToNamedType, file });
if (declaration == null) {
throw new Error(
"Cannot find declaration of type: " + referenceToNamedType + " in file " + file.relativeFilepath
Expand All @@ -54,13 +54,13 @@ export class TypeResolverImpl implements TypeResolver {
return declaration;
}

public getDeclarationOfNamedType({
public async getDeclarationOfNamedType({
referenceToNamedType,
file
}: {
referenceToNamedType: string;
file: FernFileContext;
}): RawTypeDeclarationInfo | undefined {
}): Promise<RawTypeDeclarationInfo | undefined> {
const parsedReference = parseReferenceToTypeName({
reference: referenceToNamedType,
referencedIn: file.relativeFilepath,
Expand All @@ -86,7 +86,7 @@ export class TypeResolverImpl implements TypeResolver {
relativeFilepath: parsedReference.relativeFilepath,
definitionFile,
casingsGenerator: file.casingsGenerator,
rootApiFile: this.workspace.definition.rootApiFile.contents
rootApiFile: (await this.workspace.getDefinition()).rootApiFile.contents
})
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export interface ObjectPropertyPathPart {

export type TypeName = string;

export function getAllPropertiesForObject({
export async function getAllPropertiesForObject({
typeName,
objectDeclaration,
filepathOfDeclaration,
Expand All @@ -57,7 +57,7 @@ export function getAllPropertiesForObject({
// these are for recursive calls only
path?: ObjectPropertyPath;
seen?: Record<RelativeFilePath, Set<TypeName>>;
}): ObjectPropertyWithPath[] {
}): Promise<ObjectPropertyWithPath[]> {
const properties: ObjectPropertyWithPath[] = [];

// prevent infinite looping
Expand All @@ -82,7 +82,7 @@ export function getAllPropertiesForObject({
relativeFilepath: filepathOfDeclaration,
definitionFile,
casingsGenerator,
rootApiFile: workspace.definition.rootApiFile.contents
rootApiFile: (await workspace.getDefinition()).rootApiFile.contents
});

if (objectDeclaration.properties != null) {
Expand Down Expand Up @@ -122,7 +122,7 @@ export function getAllPropertiesForObject({
const definitionFile = getDefinitionFile(workspace, resolvedTypeOfExtension.filepath);
if (definitionFile != null) {
properties.push(
...getAllPropertiesForObject({
...(await getAllPropertiesForObject({
typeName: resolvedTypeOfExtension.rawName,
objectDeclaration: resolvedTypeOfExtension.declaration,
filepathOfDeclaration: resolvedTypeOfExtension.filepath,
Expand All @@ -144,7 +144,7 @@ export function getAllPropertiesForObject({
)
],
seen
})
}))
);
}
}
Expand All @@ -154,7 +154,7 @@ export function getAllPropertiesForObject({
return properties;
}

export function getAllPropertiesForType({
export async function getAllPropertiesForType({
typeName,
filepathOfDeclaration,
definitionFile,
Expand All @@ -168,20 +168,20 @@ export function getAllPropertiesForType({
workspace: FernWorkspace;
typeResolver: TypeResolver;
smartCasing: boolean;
}): ObjectPropertyWithPath[] {
}): Promise<ObjectPropertyWithPath[]> {
const resolvedType = typeResolver.resolveNamedType({
referenceToNamedType: typeName,
file: constructFernFileContext({
relativeFilepath: filepathOfDeclaration,
definitionFile,
casingsGenerator: CASINGS_GENERATOR,
rootApiFile: workspace.definition.rootApiFile.contents
rootApiFile: (await workspace.getDefinition()).rootApiFile.contents
})
});
if (resolvedType == null || resolvedType._type !== "named" || !isRawObjectDefinition(resolvedType.declaration)) {
return [];
}
return getAllPropertiesForObject({
return await getAllPropertiesForObject({
typeName,
objectDeclaration: resolvedType.declaration,
filepathOfDeclaration,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export async function writeFilesToDiskAndRunGenerator({
const absolutePathToTmpOutputDirectory = AbsoluteFilePath.of(tmpOutputDirectory.path);
context.logger.debug("Will write output to: " + absolutePathToTmpOutputDirectory);

const absolutePathToFernDefinition = workspace.definition.absoluteFilepath;
const absolutePathToFernDefinition = (await workspace.getDefinition()).absoluteFilepath;

let absolutePathToTmpSnippetJSON = undefined;
if (absolutePathToLocalSnippetJSON != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ async function createJob({
const remoteGenerationService = createFiddleService({ token: token.value });

let fernDefinitionMetadata: FernFiddle.remoteGen.FernDefinitionMetadata | undefined;
const workspaceDefinition = await workspace.getDefinition();
// Only write definition if output mode is github
if (generatorInvocation.outputMode.type.startsWith("github")) {
try {
Expand All @@ -127,11 +128,11 @@ async function createJob({
absolutePathToTmpDefinitionDirectory,
RelativeFilePath.of(ROOT_API_FILENAME)
);
await writeFile(absolutePathToApiYml, yaml.dump(workspace.definition.rootApiFile.contents));
await writeFile(absolutePathToApiYml, yaml.dump(workspaceDefinition.rootApiFile.contents));
// write definition
await writeFernDefinition({
absolutePathToDefinitionDirectory: absolutePathToTmpDefinitionDirectory,
definition: workspace.definition
definition: workspaceDefinition
});
// write fern.config.json
const absolutePathToFernConfigJson = join(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ describe("loadWorkspace", () => {
assert(workspace.didSucceed);
assert(workspace.workspace.type === "fern");

const simpleYaml = workspace.workspace.definition.namedDefinitionFiles[RelativeFilePath.of("simple.yml")];
const simpleYaml = (await workspace.workspace.getDefinition()).namedDefinitionFiles[
RelativeFilePath.of("simple.yml")
];
const exampleDateTime = (simpleYaml?.contents.types?.MyDateTime as RawSchemas.BaseTypeDeclarationSchema)
.examples?.[0]?.value;
expect(typeof exampleDateTime).toBe("string");
Expand Down
Loading
Loading