Skip to content

Commit

Permalink
use namespace from code-model
Browse files Browse the repository at this point in the history
  • Loading branch information
weidongxu-microsoft committed Nov 11, 2024
1 parent c7dc05a commit 7fe1493
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 24 deletions.
111 changes: 87 additions & 24 deletions packages/http-client-java/emitter/src/code-model-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ export class CodeModelBuilder {
private program: Program;
private typeNameOptions: TypeNameOptions;
private namespace: string;
private baseJavaNamespace: string;
// private legacyJavaNamespace: boolean;
private sdkContext!: SdkContext;
private options: EmitterOptions;
private codeModel: CodeModel;
Expand Down Expand Up @@ -206,7 +208,8 @@ export class CodeModelBuilder {

this.namespace = getNamespaceFullName(this.serviceNamespace) || "Azure.Client";
// java namespace
const javaNamespace = this.getJavaNamespace(this.namespace);
this.baseJavaNamespace = this.getJavaNamespace(this.namespace)!;
// this.legacyJavaNamespace = this.options.namespace != undefined && this.options.namespace.length > 0;

const namespace1 = this.namespace;
this.typeNameOptions = {
Expand All @@ -233,7 +236,7 @@ export class CodeModelBuilder {
namespace: this.namespace,
},
java: {
namespace: javaNamespace,
namespace: this.baseJavaNamespace,
},
},
});
Expand Down Expand Up @@ -267,6 +270,8 @@ export class CodeModelBuilder {

this.processSchemaUsage();

this.processJavaNamespace();

this.deduplicateSchemaName();

return this.codeModel;
Expand Down Expand Up @@ -424,6 +429,16 @@ export class CodeModelBuilder {
this.codeModel.schemas.constants?.forEach((it) => this.resolveSchemaUsage(it));
}

private processJavaNamespace() {
// post process for usage and namespace ("implementation.models")
this.codeModel.schemas.objects?.forEach((it) => this.updateJavaNamespace(it));
this.codeModel.schemas.groups?.forEach((it) => this.updateJavaNamespace(it));
this.codeModel.schemas.choices?.forEach((it) => this.updateJavaNamespace(it));
this.codeModel.schemas.sealedChoices?.forEach((it) => this.updateJavaNamespace(it));
this.codeModel.schemas.ors?.forEach((it) => this.updateJavaNamespace(it));
this.codeModel.schemas.constants?.forEach((it) => this.updateJavaNamespace(it));
}

private deduplicateSchemaName() {
// deduplicate model name
const nameCount = new Map<string, number>();
Expand Down Expand Up @@ -483,20 +498,47 @@ export class CodeModelBuilder {
}
}

private updateJavaNamespace(schema: Schema) {
if (
schema instanceof ObjectSchema ||
schema instanceof GroupSchema ||
schema instanceof ChoiceSchema ||
schema instanceof SealedChoiceSchema ||
schema instanceof OrSchema ||
schema instanceof ConstantSchema
) {
const schemaUsage: SchemaContext[] | undefined = schema.usage;

if (schema.language.java?.namespace) {
if (this.isBranded() && schema.language.java.namespace.startsWith("com.azure.")) {
// skip com.azure models
return;
}

if (schemaUsage?.includes(SchemaContext.Internal)) {
schema.language.java.namespace = schema.language.java.namespace + ".implementation";
}
if (this.isBranded()) {
schema.language.java.namespace = schema.language.java.namespace + ".models";
}
}
}
}

private processClients() {
// preprocess group-etag-headers
this.options["group-etag-headers"] = this.options["group-etag-headers"] ?? true;

const sdkPackage = this.sdkContext.sdkPackage;
for (const client of sdkPackage.clients) {
let clientName = client.name;
let javaNamespace = this.getJavaNamespace(this.namespace);
let javaNamespace = this.baseJavaNamespace;
const clientFullName = client.name;
const clientNameSegments = clientFullName.split(".");
if (clientNameSegments.length > 1) {
clientName = clientNameSegments.at(-1)!;
const clientSubNamespace = clientNameSegments.slice(0, -1).join(".");
javaNamespace = this.getJavaNamespace(this.namespace + "." + clientSubNamespace);
const clientSubNamespace = clientNameSegments.slice(0, -1).join(".").toLowerCase();
javaNamespace = this.baseJavaNamespace + "." + clientSubNamespace;
}

const codeModelClient = new CodeModelClient(clientName, client.doc ?? "", {
Expand Down Expand Up @@ -955,7 +997,7 @@ export class CodeModelBuilder {
language: {
java: {
name: "OperationLocationPollingStrategy",
namespace: this.getJavaNamespace(this.namespace) + ".implementation",
namespace: this.baseJavaNamespace + ".implementation",
},
},
});
Expand Down Expand Up @@ -1952,7 +1994,7 @@ export class CodeModelBuilder {
namespace: namespace,
},
java: {
namespace: this.getJavaNamespace(namespace),
namespace: this.getJavaNamespace(namespace, type),
},
},
});
Expand Down Expand Up @@ -2052,7 +2094,7 @@ export class CodeModelBuilder {
namespace: namespace,
},
java: {
namespace: this.getJavaNamespace(namespace),
namespace: this.getJavaNamespace(namespace, type),
},
},
});
Expand Down Expand Up @@ -2234,7 +2276,7 @@ export class CodeModelBuilder {
namespace: namespace,
},
java: {
namespace: this.getJavaNamespace(namespace),
namespace: this.getJavaNamespace(namespace, it),
},
},
});
Expand Down Expand Up @@ -2320,15 +2362,21 @@ export class CodeModelBuilder {

private processMultipartFormDataFilePropertySchema(property: SdkBodyModelPropertyType): Schema {
const processSchemaFunc = (type: SdkType) => this.processSchema(type, "");
if (property.type.kind === "bytes" || property.type.kind === "model") {
const processNamespaceFunc = (type: SdkType) => {
const namespace =
property.type.kind === "model"
type.kind === "model"
? (getNamespace(property.type.__raw) ?? this.namespace)
: this.namespace;
const javaNamespace = this.getJavaNamespace(namespace, type);
return { namespace, javaNamespace };
};

if (property.type.kind === "bytes" || property.type.kind === "model") {
const namespaceTuple = processNamespaceFunc(property.type);
return getFileDetailsSchema(
property,
getNamespace(property.type.__raw) ?? this.namespace,
namespace,
namespaceTuple.namespace,
namespaceTuple.javaNamespace,
this.codeModel.schemas,
this.binarySchema,
this.stringSchema,
Expand All @@ -2338,17 +2386,14 @@ export class CodeModelBuilder {
property.type.kind === "array" &&
(property.type.valueType.kind === "bytes" || property.type.valueType.kind === "model")
) {
const namespace =
property.type.valueType.kind === "model"
? (getNamespace(property.type.valueType.__raw) ?? this.namespace)
: this.namespace;
const namespaceTuple = processNamespaceFunc(property.type.valueType);
return new ArraySchema(
property.name,
property.doc ?? "",
getFileDetailsSchema(
property,
namespace,
this.getJavaNamespace(namespace),
namespaceTuple.namespace,
namespaceTuple.javaNamespace,
this.codeModel.schemas,
this.binarySchema,
this.stringSchema,
Expand Down Expand Up @@ -2444,19 +2489,37 @@ export class CodeModelBuilder {
}
}

private getJavaNamespace(namespace: string | undefined): string | undefined {
private getJavaNamespace(
namespace: string | undefined,
type: SdkType | SdkClientType<SdkHttpOperation> | undefined = undefined,
): string | undefined {
const tspNamespace = this.namespace;
const baseJavaNamespace = this.emitterContext.options.namespace;

if (!namespace) {
return undefined;
} else if (
}

if (baseJavaNamespace && type && "crossLanguageDefinitionId" in type) {
if (type.crossLanguageDefinitionId === "TypeSpec.Http.File") {
// special handling for TypeSpec.Http.File
return this.baseJavaNamespace;
} else if (type.crossLanguageDefinitionId === "Azure.Core.OperationState") {
// special handling for Azure.Core.OperationState
return this.baseJavaNamespace;
}
}

if (
baseJavaNamespace &&
(namespace === tspNamespace || namespace.startsWith(tspNamespace + "."))
) {
return baseJavaNamespace + namespace.slice(tspNamespace.length).toLowerCase();
} else {
return "com." + namespace.toLowerCase();
// make sure the mapping of typespec service namespace to options.namespace is maintained
// e.g. "Microsoft.StandbyPool" to "com.azure.resourcemanager.standbypool"
return baseJavaNamespace;
}

return "com." + namespace.toLowerCase();
}

private logWarning(msg: string) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ public static IType createEnumType(ChoiceSchema enumType, boolean expandable, bo
String enumPackage = settings.getPackage(settings.getModelsSubpackage());
if (settings.isCustomType(enumTypeName)) {
enumPackage = settings.getPackage(settings.getCustomTypesSubpackage());
} else if (settings.isDataPlaneClient() && enumType.getLanguage().getJava().getNamespace() != null) {
enumPackage = enumType.getLanguage().getJava().getNamespace();
} else if (settings.isDataPlaneClient()
&& (enumType.getUsage() != null && enumType.getUsage().contains(SchemaContext.INTERNAL))) {
// internal type, which is not exposed to user
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ private ClassType createClassType(ObjectSchema compositeType) {
} else if (settings.isFluent() && compositeType.isFlattenedSchema()) {
// put class of flattened type to implementation package
classPackage = settings.getPackage(settings.getFluentModelsSubpackage());
} else if (settings.isDataPlaneClient() && compositeType.getLanguage().getJava().getNamespace() != null) {
classPackage = compositeType.getLanguage().getJava().getNamespace();
} else if (settings.isDataPlaneClient() && isInternalModel(compositeType)) {
// internal type is not exposed to user
classPackage = settings.getPackage(settings.getImplementationSubpackage(), settings.getModelsSubpackage());
Expand Down

0 comments on commit 7fe1493

Please sign in to comment.