Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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 @@ -208,6 +208,7 @@ function fromSdkModelType(
const inputModelType: InputModelType = {
kind: "model",
name: modelType.name,
apiVersions: modelType.apiVersions,
namespace: modelType.namespace,
crossLanguageDefinitionId: modelType.crossLanguageDefinitionId,
access: getAccessOverride(sdkContext, modelType.__raw as Model),
Expand Down Expand Up @@ -281,6 +282,7 @@ function fromSdkModelProperty(
property = {
kind: sdkProperty.kind,
name: sdkProperty.name,
apiVersions: sdkProperty.apiVersions,
serializedName: serializedName,
summary: sdkProperty.summary,
doc: sdkProperty.doc,
Expand Down Expand Up @@ -346,6 +348,7 @@ function createEnumType(
const inputEnumType: InputEnumType = {
kind: "enum",
name: sdkType.name,
apiVersions: sdkType.kind === "enum" ? sdkType.apiVersions : [],
crossLanguageDefinitionId: sdkType.kind === "enum" ? sdkType.crossLanguageDefinitionId : "",
valueType:
sdkType.kind === "enum"
Expand Down
3 changes: 3 additions & 0 deletions packages/http-client-csharp/emitter/src/type/input-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ export function isInputUnionType(type: InputType): type is InputUnionType {
export interface InputModelType extends InputTypeBase {
kind: "model";
properties: InputModelProperty[];
apiVersions: string[];
name: string;
crossLanguageDefinitionId: string;
access?: AccessFlags;
Expand Down Expand Up @@ -192,6 +193,7 @@ export interface InputPropertyTypeBase extends DecoratedType {

export interface InputModelProperty extends InputPropertyTypeBase {
kind: "property";
apiVersions: string[];
discriminator: boolean;
serializedName: string;
serializationOptions: SerializationOptions;
Expand Down Expand Up @@ -270,6 +272,7 @@ export interface InputEndpointParameter extends InputPropertyTypeBase {

export interface InputEnumType extends InputTypeBase {
kind: "enum";
apiVersions: string[];
name: string;
crossLanguageDefinitionId: string;
valueType: InputPrimitiveType;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
vi.resetModules();

import type { TestHost } from "@typespec/compiler/testing";
import { ok, strictEqual } from "assert";
import { deepStrictEqual, ok, strictEqual } from "assert";
import { beforeEach, describe, it, vi } from "vitest";
import { createModel } from "../../src/lib/client-model-builder.js";
import type { InputEnumType } from "../../src/type/input-type.js";
Expand Down Expand Up @@ -505,6 +505,71 @@ describe("parseApiVersions", () => {
strictEqual(root.apiVersions[1], "2024-01-01", "Second version should be 2024-01-01");
strictEqual(root.apiVersions[2], "2025-01-01", "Third version should be 2025-01-01");
});

it("should preserve API versions for models, enums, and model properties", async () => {
const program = await typeSpecCompile(
`
@service(#{ title: "Test Service" })
@versioned(Versions)
namespace TestService;

enum Versions {
v1: "2024-01-01",
v2: "2024-06-01-preview",
}

model StableModel {
stableProperty: string;

@added(Versions.v2)
previewProperty: string;
}

@added(Versions.v2)
model PreviewModel {
status: PreviewEnum;
}

@added(Versions.v2)
enum PreviewEnum {
enabled,
}

@route("/test")
@added(Versions.v2)
op test(@body body: StableModel): PreviewModel;
`,
runner,
{ IsNamespaceNeeded: false },
);
const context = createEmitterContext(program);
const sdkContext = await createCSharpSdkContext(context);
const [root] = createModel(sdkContext);

const stableModel = root.models.find((model) => model.name === "StableModel");
ok(stableModel);
deepStrictEqual(stableModel.apiVersions, ["2024-01-01", "2024-06-01-preview"]);

const stableProperty = stableModel.properties.find(
(property) => property.name === "stableProperty",
);
ok(stableProperty);
deepStrictEqual(stableProperty.apiVersions, ["2024-01-01", "2024-06-01-preview"]);

const previewProperty = stableModel.properties.find(
(property) => property.name === "previewProperty",
);
ok(previewProperty);
deepStrictEqual(previewProperty.apiVersions, ["2024-06-01-preview"]);

const previewModel = root.models.find((model) => model.name === "PreviewModel");
ok(previewModel);
deepStrictEqual(previewModel.apiVersions, ["2024-06-01-preview"]);

const previewEnum = root.enums.find((enumType) => enumType.name === "PreviewEnum");
ok(previewEnum);
deepStrictEqual(previewEnum.apiVersions, ["2024-06-01-preview"]);
});
});

describe("createModel diagnostic collection", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class InputEnumType : InputType
{
// We always call the Values setter so we know the field will not be null.
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
public InputEnumType(string name, string @namespace, string crossLanguageDefinitionId, string? access, string? deprecation, string? summary, string? doc, InputModelTypeUsage usage, InputPrimitiveType valueType, IReadOnlyList<InputEnumTypeValue> values, bool isExtensible)
public InputEnumType(string name, string @namespace, string crossLanguageDefinitionId, string? access, string? deprecation, string? summary, string? doc, InputModelTypeUsage usage, InputPrimitiveType valueType, IReadOnlyList<InputEnumTypeValue> values, bool isExtensible, IReadOnlyList<string>? apiVersions = null)
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
Comment on lines 11 to 13
: base(name)
{
Expand All @@ -23,6 +23,7 @@ public InputEnumType(string name, string @namespace, string crossLanguageDefinit
ValueType = valueType;
Values = values;
IsExtensible = isExtensible;
ApiVersions = apiVersions ?? [];
}

public string Namespace { get; internal set; }
Expand All @@ -32,6 +33,7 @@ public InputEnumType(string name, string @namespace, string crossLanguageDefinit
public string? Summary { get; internal set; }
public string? Doc { get; internal set; }
public InputModelTypeUsage Usage { get; internal set; }
public IReadOnlyList<string> ApiVersions { get; internal set; }
public InputPrimitiveType ValueType { get; internal set; }
private IReadOnlyList<InputEnumTypeValue> _values;
public IReadOnlyList<InputEnumTypeValue> Values
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Collections.Generic;
using Microsoft.TypeSpec.Generator.Input.Extensions;

namespace Microsoft.TypeSpec.Generator.Input
Expand All @@ -21,7 +22,8 @@ public InputModelProperty(
bool isApiVersion,
InputConstant? defaultValue,
InputSerializationOptions serializationOptions,
ArrayKnownEncoding? encode = null)
ArrayKnownEncoding? encode = null,
IReadOnlyList<string>? apiVersions = null)
: base(name, summary, doc, type, isRequired, isReadOnly, access, serializedName, isApiVersion, defaultValue)
{
Name = name;
Expand All @@ -34,12 +36,14 @@ public InputModelProperty(
IsHttpMetadata = isHttpMetadata;
SerializationOptions = serializationOptions;
Encode = encode;
ApiVersions = apiVersions ?? [];
}

public bool IsDiscriminator { get; internal set; }
public InputSerializationOptions? SerializationOptions { get; internal set; }
public bool IsHttpMetadata { get; internal set; }
public ArrayKnownEncoding? Encode { get; internal set; }
public IReadOnlyList<string> ApiVersions { get; internal set; }

/// <summary>
/// Updates the properties of the input model property.
Expand All @@ -54,6 +58,7 @@ public InputModelProperty(
/// <param name="isDiscriminator">The new discriminator status for the property.</param>
/// <param name="serializedName">The new serialized name for the property.</param>
/// <param name="serializationOptions">The new serialization options for the property.</param>
/// <param name="apiVersions">The new API versions for the property.</param>
public void Update(
string? name = null,
string? summary = null,
Expand All @@ -65,7 +70,8 @@ public void Update(
bool? isDiscriminator = null,
string? serializedName = null,
bool? isHttpMetadata = null,
InputSerializationOptions? serializationOptions = null)
InputSerializationOptions? serializationOptions = null,
IEnumerable<string>? apiVersions = null)
{
if (name != null)
{
Expand Down Expand Up @@ -121,6 +127,11 @@ public void Update(
{
SerializationOptions = serializationOptions;
}

if (apiVersions != null)
{
ApiVersions = [.. apiVersions];
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class InputModelType : InputType
private IList<InputModelType> _derivedModels = [];

// TODO: Follow up issue https://github.com/microsoft/typespec/issues/3619. After https://github.com/Azure/typespec-azure/pull/966 is completed, update this type and remove the "modelAsStruct" parameter.
public InputModelType(string name, string @namespace, string crossLanguageDefinitionId, string? access, string? deprecation, string? summary, string? doc, InputModelTypeUsage usage, IReadOnlyList<InputModelProperty> properties, InputModelType? baseModel, IReadOnlyList<InputModelType> derivedModels, string? discriminatorValue, InputModelProperty? discriminatorProperty, IReadOnlyDictionary<string, InputModelType> discriminatedSubtypes, InputType? additionalProperties, bool modelAsStruct, InputSerializationOptions serializationOptions, bool isDynamicModel)
public InputModelType(string name, string @namespace, string crossLanguageDefinitionId, string? access, string? deprecation, string? summary, string? doc, InputModelTypeUsage usage, IReadOnlyList<InputModelProperty> properties, InputModelType? baseModel, IReadOnlyList<InputModelType> derivedModels, string? discriminatorValue, InputModelProperty? discriminatorProperty, IReadOnlyDictionary<string, InputModelType> discriminatedSubtypes, InputType? additionalProperties, bool modelAsStruct, InputSerializationOptions serializationOptions, bool isDynamicModel, IReadOnlyList<string>? apiVersions = null)
: base(name)
{
Namespace = @namespace;
Expand Down Expand Up @@ -48,6 +48,7 @@ public InputModelType(string name, string @namespace, string crossLanguageDefini
IsPropertyBag = false;
ModelAsStruct = modelAsStruct;
SerializationOptions = serializationOptions;
ApiVersions = apiVersions ?? [];
}

public string Namespace { get; internal set; }
Expand All @@ -57,6 +58,7 @@ public InputModelType(string name, string @namespace, string crossLanguageDefini
public string? Summary { get; internal set; }
public string? Doc { get; internal set; }
public InputModelTypeUsage Usage { get; internal set; }
public IReadOnlyList<string> ApiVersions { get; internal set; }

public IReadOnlyList<InputModelProperty> Properties
{
Expand Down Expand Up @@ -126,7 +128,8 @@ internal set
null,
false,
SerializationOptions,
IsDynamicModel)
IsDynamicModel,
ApiVersions)
);
}
}
Expand Down Expand Up @@ -173,6 +176,7 @@ private static IEnumerable<InputModelType> EnumerateBase(InputModelType? model)
/// <param name="doc">The new documentation for the model.</param>
/// <param name="usage">The new usage for the model.</param>
/// <param name="properties">The new properties for the model.</param>
/// <param name="apiVersions">The new API versions for the model.</param>
/// <param name="baseModel">The new base model for the model.</param>
/// <param name="discriminatorValue">The new discriminator value for the model.</param>
/// <param name="discriminatorProperty">The new discriminator property for the model.</param>
Expand All @@ -196,7 +200,8 @@ public void Update(
InputType? additionalProperties = null,
bool? modelAsStruct = null,
InputSerializationOptions? serializationOptions = null,
bool? isDynamicModel = null)
bool? isDynamicModel = null,
IEnumerable<string>? apiVersions = null)
{
if (name != null)
{
Expand Down Expand Up @@ -243,6 +248,11 @@ public void Update(
Properties = [.. properties];
}

if (apiVersions != null)
{
ApiVersions = [.. apiVersions];
}

if (baseModel != null)
{
BaseModel = baseModel;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ public static InputEnumType CreateEnumType(ref Utf8JsonReader reader, string? id
usage: InputModelTypeUsage.None,
valueType: null!,
values: Array.Empty<InputEnumTypeValue>(),
isExtensible: false);
isExtensible: false,
apiVersions: []);
resolver.AddReference(id, enumType);

string? @namespace = null;
Expand All @@ -62,6 +63,7 @@ public static InputEnumType CreateEnumType(ref Utf8JsonReader reader, string? id
IReadOnlyList<InputDecoratorInfo>? decorators = null;
InputExternalTypeMetadata? external = null;
bool isExactName = false;
IReadOnlyList<string>? apiVersions = null;
while (reader.TokenType != JsonTokenType.EndObject)
{
var isKnownProperty = reader.TryReadString("name", ref name)
Expand All @@ -72,6 +74,7 @@ public static InputEnumType CreateEnumType(ref Utf8JsonReader reader, string? id
|| reader.TryReadString("summary", ref summary)
|| reader.TryReadString("doc", ref doc)
|| reader.TryReadString("usage", ref usageString)
|| reader.TryReadComplexType("apiVersions", options, ref apiVersions)
|| reader.TryReadBoolean("isFixed", ref isFixed)
|| reader.TryReadComplexType("valueType", options, ref valueType)
|| reader.TryReadComplexType("values", options, ref values)
Expand Down Expand Up @@ -103,6 +106,7 @@ public static InputEnumType CreateEnumType(ref Utf8JsonReader reader, string? id
enumType.Decorators = decorators ?? [];
enumType.External = external;
enumType.IsExactName = isExactName;
enumType.ApiVersions = apiVersions ?? [];

return enumType;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ internal static InputModelProperty ReadInputModelProperty(ref Utf8JsonReader rea
isApiVersion: false,
defaultValue: null,
serializationOptions: null!,
encode: null);
encode: null,
apiVersions: []);
resolver.AddReference(id, property);

string? kind = null;
Expand All @@ -66,12 +67,14 @@ internal static InputModelProperty ReadInputModelProperty(ref Utf8JsonReader rea
InputSerializationOptions? serializationOptions = null;
string? encodeString = null;
bool isExactName = false;
IReadOnlyList<string>? apiVersions = null;

while (reader.TokenType != JsonTokenType.EndObject)
{
var isKnownProperty = reader.TryReadReferenceId(ref id)
|| reader.TryReadString("name", ref name)
|| reader.TryReadString("kind", ref kind)
|| reader.TryReadComplexType("apiVersions", options, ref apiVersions)
|| reader.TryReadString("summary", ref summary)
|| reader.TryReadString("doc", ref doc)
|| reader.TryReadComplexType("type", options, ref propertyType)
Expand Down Expand Up @@ -110,6 +113,7 @@ internal static InputModelProperty ReadInputModelProperty(ref Utf8JsonReader rea
property.DefaultValue = defaultValue;
property.Encode = Enum.TryParse<ArrayKnownEncoding>(encodeString, ignoreCase: true, out var encode) ? encode : null;
property.IsExactName = isExactName;
property.ApiVersions = apiVersions ?? [];

return property;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ internal static InputModelType CreateModelType(ref Utf8JsonReader reader, string
additionalProperties: null,
modelAsStruct: false,
serializationOptions: null!,
isDynamicModel: false);
isDynamicModel: false,
apiVersions: []);
resolver.AddReference(id, model);

string? @namespace = null;
Expand All @@ -75,6 +76,7 @@ internal static InputModelType CreateModelType(ref Utf8JsonReader reader, string
InputExternalTypeMetadata? external = null;
bool isExactName = false;
bool isFileType = false;
IReadOnlyList<string>? apiVersions = null;

// read all possible properties and throw away the unknown properties
while (reader.TokenType != JsonTokenType.EndObject)
Expand All @@ -87,6 +89,7 @@ internal static InputModelType CreateModelType(ref Utf8JsonReader reader, string
|| reader.TryReadString("summary", ref doc)
|| reader.TryReadString("doc", ref doc)
|| reader.TryReadString("usage", ref usageString)
|| reader.TryReadComplexType("apiVersions", options, ref apiVersions)
|| reader.TryReadComplexType("discriminatorProperty", options, ref discriminatorProperty)
|| reader.TryReadString("discriminatorValue", ref discriminatorValue)
|| reader.TryReadComplexType("additionalProperties", options, ref additionalProperties)
Expand Down Expand Up @@ -120,6 +123,7 @@ internal static InputModelType CreateModelType(ref Utf8JsonReader reader, string
parsedUsage |= InputModelTypeUsage.Json;
}
model.Usage = parsedUsage;
model.ApiVersions = apiVersions ?? [];
model.DiscriminatorValue = discriminatorValue;
model.DiscriminatorProperty = discriminatorProperty;
model.AdditionalProperties = additionalProperties;
Expand Down
Loading
Loading