Skip to content

Commit

Permalink
Merge branch 'main' into feature/typescript/composed-types
Browse files Browse the repository at this point in the history
  • Loading branch information
koros committed May 16, 2024
2 parents dfe28b1 + 2f2d203 commit 6a07f54
Show file tree
Hide file tree
Showing 21 changed files with 307 additions and 179 deletions.
4 changes: 3 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,9 @@
"--type",
"ApiManifest",
"--type",
"microsoft"
"microsoft",
"--type",
"OpenAI"
],
"cwd": "${workspaceFolder}/samples/msgraph-mail/dotnet",
"console": "internalConsole",
Expand Down
7 changes: 5 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added missing nullable directives for CLI generation.
- Fixed handling of nested arrays to be handled as `UntypedNode` instances [#4549](https://github.com/microsoft/kiota/issues/4549)
- Fixed `InvalidOperationException` thrown when serializing IBacked models with no changes present in the additional data in dotnet [microsoftgraph/msgraph-sdk-dotnet#2471](https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/2471).
- Fixed a bug where enums could be considered inheritance parents. [#4640](https://github.com/microsoft/kiota/issues/4640)
- Fixed `RequestConfiguration` Classes dropped in RequestBuilder methods in python [#4535](https://github.com/microsoft/kiota/issues/4535)
- Fixed incorrect optional types in method parameters in Python [#4507](https://github.com/microsoft/kiota/issues/4507)
- Changed enum parsing methods to return nil in the default case in Go [#4621](https://github.com/microsoft/kiota/issues/4621)
Expand All @@ -31,9 +32,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed a bug where multiple allOf entries type would not get merged if they were part of a discriminator. [#4325](https://github.com/microsoft/kiota/issues/4325)
- Fixed a bug where allOf structure with one entry and properties would not be considered as inheritance case. [#4346](https://github.com/microsoft/kiota/issues/4346)
- Fixed a bug where some allOf scenarios would be missing properties if type object wasn't set on the schema. [#4074](https://github.com/microsoft/kiota/issues/4074)
- Fixed a bug where schema with multiple allOf entries would incorrectly get merged to inherit from the first entry [#4428] (https://github.com/microsoft/kiota/issues/4428)
- Fixed a bug where schema with multiple allOf entries would incorrectly get merged to inherit from the first entry [#4428](https://github.com/microsoft/kiota/issues/4428)
- Fixes constructor generation for nullable properties that are initialized as null in C#,Java and PHP
- Fixed a bug where the hash alias in typescript wasn't being generated uniformly for similar interfaces [#84](https://github.com/microsoftgraph/msgraph-beta-sdk-typescript/issues/84)
- Fixed a bug where the hash alias in typescript wasn't being generated uniformly for similar interfaces [microsoftgraph/msgraph-beta-sdk-typescript#84](https://github.com/microsoftgraph/msgraph-beta-sdk-typescript/issues/84)
- Fixes a bug where name collisions would occur in the Typescript refiner if model name also exists with the `Interface` suffix [#4382](https://github.com/microsoft/kiota/issues/4382)


## [1.14.0] - 2024-05-02

Expand Down
192 changes: 96 additions & 96 deletions it/typescript/package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion it/typescript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"@types/node": "^20.12.12",
"@typescript-eslint/eslint-plugin": "^7.7.1",
"@typescript-eslint/parser": "^7.8.0",
"esbuild": "^0.21.2",
"esbuild": "^0.21.3",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"minimist": "^1.2.8",
Expand Down
4 changes: 2 additions & 2 deletions src/Kiota.Builder/CodeDOM/CodeInterface.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ public enum CodeInterfaceKind

public class CodeInterface : ProprietableBlock<CodeInterfaceKind, InterfaceDeclaration>, ITypeDefinition, IDeprecableElement
{
public CodeClass? OriginalClass
public required CodeClass OriginalClass
{
get; set;
get; init;
}
public DeprecationInformation? Deprecation
{
Expand Down
12 changes: 6 additions & 6 deletions src/Kiota.Builder/Extensions/OpenApiSchemaExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ public static bool IsInclusiveUnion(this OpenApiSchema? schema)
public static bool IsInherited(this OpenApiSchema? schema)
{
if (schema is null) return false;
var meaningfulMemberSchemas = schema.AllOf.FlattenSchemaIfRequired(static x => x.AllOf).Where(static x => x.IsSemanticallyMeaningful()).ToArray();
var isRootSchemaMeaningful = schema.IsSemanticallyMeaningful();
var meaningfulMemberSchemas = schema.AllOf.FlattenSchemaIfRequired(static x => x.AllOf).Where(static x => x.IsSemanticallyMeaningful(ignoreEnums: true, ignoreArrays: true, ignoreType: true)).ToArray();
var isRootSchemaMeaningful = schema.IsSemanticallyMeaningful(ignoreEnums: true, ignoreArrays: true, ignoreType: true);
return meaningfulMemberSchemas.Count(static x => !string.IsNullOrEmpty(x.Reference?.Id)) == 1 &&
(meaningfulMemberSchemas.Count(static x => string.IsNullOrEmpty(x.Reference?.Id)) == 1 ||
isRootSchemaMeaningful);
Expand Down Expand Up @@ -141,13 +141,13 @@ public static bool IsComposedEnum(this OpenApiSchema schema)
return schema.AnyOf.Count(static x => !x.IsSemanticallyMeaningful(true)) == 1 && schema.AnyOf.Count(static x => x.IsEnum()) == 1 ||
schema.OneOf.Count(static x => !x.IsSemanticallyMeaningful(true)) == 1 && schema.OneOf.Count(static x => x.IsEnum()) == 1;
}
public static bool IsSemanticallyMeaningful(this OpenApiSchema schema, bool ignoreNullableObjects = false)
public static bool IsSemanticallyMeaningful(this OpenApiSchema schema, bool ignoreNullableObjects = false, bool ignoreEnums = false, bool ignoreArrays = false, bool ignoreType = false)
{
if (schema is null) return false;
return schema.HasAnyProperty() ||
schema.Enum is { Count: > 0 } ||
schema.Items != null ||
(!string.IsNullOrEmpty(schema.Type) &&
(!ignoreEnums && schema.Enum is { Count: > 0 }) ||
(!ignoreArrays && schema.Items != null) ||
(!ignoreType && !string.IsNullOrEmpty(schema.Type) &&
((ignoreNullableObjects && !"object".Equals(schema.Type, StringComparison.OrdinalIgnoreCase)) ||
!ignoreNullableObjects)) ||
!string.IsNullOrEmpty(schema.Format) ||
Expand Down
2 changes: 1 addition & 1 deletion src/Kiota.Builder/Kiota.Builder.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
<PackageReference Include="Microsoft.OpenApi" Version="1.6.14" />
<PackageReference Include="Microsoft.OpenApi.ApiManifest" Version="0.5.5-preview" />
<PackageReference Include="Microsoft.OpenApi.Readers" Version="1.6.14" />
<PackageReference Include="Microsoft.Plugins.Manifest" Version="0.0.6-preview" />
<PackageReference Include="Microsoft.Plugins.Manifest" Version="0.0.7-preview" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
<PackageReference Include="YamlDotNet" Version="15.1.2" />
<ProjectReference Include="..\Kiota.Generated\KiotaGenerated.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
Expand Down
13 changes: 6 additions & 7 deletions src/Kiota.Builder/KiotaBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,6 @@ public async Task<bool> GeneratePluginAsync(CancellationToken cancellationToken)
{
return await GenerateConsumerAsync(async (sw, stepId, openApiTree, CancellationToken) =>
{
if (config.PluginTypes.Contains(PluginType.OpenAI))
throw new NotImplementedException("The OpenAI plugin type is not supported for generation");
if (openApiDocument is null || openApiTree is null)
throw new InvalidOperationException("The OpenAPI document and the URL tree must be loaded before generating the plugins");
// generate plugin
Expand Down Expand Up @@ -1590,9 +1588,12 @@ private CodeClass CreateInheritedModelDeclaration(OpenApiUrlTreeNode currentNode
(false, null, not null) => AddModelDeclarationIfDoesntExist(currentNode, referencedSchema, className, shortestNamespace),
// empty schema + inline schema
(false, not null, null) => AddModelDeclarationIfDoesntExist(currentNode, inlineSchema, className, shortestNamespace),
// meaningless scenarios
// too much information but we can make a choice -> maps to properties + inline schema
(true, not null, not null) when inlineSchema.HasAnyProperty() => AddModelDeclarationIfDoesntExist(currentNode, schema, className, shortestNamespace, CreateInheritedModelDeclaration(currentNode, inlineSchema, operation, classNameSuffix, codeNamespace, isRequestBody, typeNameForInlineSchema)),
// too much information but we can make a choice -> maps to properties + referenced schema
(true, not null, not null) when referencedSchema.HasAnyProperty() => AddModelDeclarationIfDoesntExist(currentNode, schema, className, shortestNamespace, CreateInheritedModelDeclaration(currentNode, referencedSchema, operation, classNameSuffix, codeNamespace, isRequestBody, string.Empty)),
// meaningless scenario
(false, null, null) or (true, not null, not null) => throw new InvalidOperationException("invalid inheritance case"),

};
if (codeDeclaration is not CodeClass currentClass) throw new InvalidOperationException("Inheritance is only supported for classes");
if (!currentClass.Documentation.DescriptionAvailable &&
Expand Down Expand Up @@ -1896,9 +1897,7 @@ private CodeClass AddModelClass(OpenApiUrlTreeNode currentNode, OpenApiSchema sc
}

var mappings = GetDiscriminatorMappings(currentNode, schema, currentNamespace, newClass)
.Where(x => x.Value is CodeType type &&
type.TypeDefinition != null &&
type.TypeDefinition is CodeClass definition &&
.Where(x => x.Value is { TypeDefinition: CodeClass definition } &&
definition.DerivesFrom(newClass)); // only the mappings that derive from the current class

AddDiscriminatorMethod(newClass, schema.GetDiscriminatorPropertyName(), mappings, static s => s);
Expand Down
105 changes: 78 additions & 27 deletions src/Kiota.Builder/Plugins/PluginsGenerationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public PluginsGenerationService(OpenApiDocument document, OpenApiUrlTreeNode ope
private static readonly OpenAPIRuntimeComparer _openAPIRuntimeComparer = new();
private const string ManifestFileNameSuffix = ".json";
private const string DescriptionPathSuffix = "openapi.yml";
private const string OpenAIManifestFileName = "openai-plugins";
public async Task GenerateManifestAsync(CancellationToken cancellationToken = default)
{
// write the description
Expand All @@ -56,7 +57,8 @@ public async Task GenerateManifestAsync(CancellationToken cancellationToken = de
// write the plugins
foreach (var pluginType in Configuration.PluginTypes)
{
var manifestOutputPath = Path.Combine(Configuration.OutputPath, $"{Configuration.ClientClassName.ToLowerInvariant()}-{pluginType.ToString().ToLowerInvariant()}{ManifestFileNameSuffix}");
var manifestFileName = pluginType == PluginType.OpenAI ? OpenAIManifestFileName : $"{Configuration.ClientClassName.ToLowerInvariant()}-{pluginType.ToString().ToLowerInvariant()}";
var manifestOutputPath = Path.Combine(Configuration.OutputPath, $"{manifestFileName}{ManifestFileNameSuffix}");
#pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task
await using var fileStream = File.Create(manifestOutputPath, 4096);
await using var writer = new Utf8JsonWriter(fileStream, new JsonWriterOptions { Indented = true });
Expand All @@ -70,51 +72,67 @@ public async Task GenerateManifestAsync(CancellationToken cancellationToken = de
break;
case PluginType.APIManifest:
var apiManifest = new ApiManifestDocument("application"); //TODO add application name
// pass empty cong hash so that its not included in this manifest.
// pass empty config hash so that its not included in this manifest.
apiManifest.ApiDependencies.AddOrReplace(Configuration.ClientClassName, Configuration.ToApiDependency(string.Empty, TreeNode?.GetRequestInfo().ToDictionary(static x => x.Key, static x => x.Value) ?? [], WorkingDirectory));
var publisherName = string.IsNullOrEmpty(OAIDocument.Info?.Contact?.Name)
? DefaultContactName
: OAIDocument.Info.Contact.Name;
var publisherEmail = string.IsNullOrEmpty(OAIDocument.Info?.Contact?.Email)
? DefaultContactEmail
: OAIDocument.Info.Contact.Email;
apiManifest.Publisher = new Publisher(publisherName, publisherEmail);
apiManifest.Write(writer);
break;
case PluginType.OpenAI://TODO add support for OpenAI plugin type generation
// intentional drop to the default case
case PluginType.OpenAI:
var pluginDocumentV1 = GetV1ManifestDocument(descriptionRelativePath);
pluginDocumentV1.Write(writer);
break;
default:
throw new NotImplementedException($"The {pluginType} plugin is not implemented.");
}
await writer.FlushAsync(cancellationToken).ConfigureAwait(false);
}
}
private PluginManifestDocument GetV1ManifestDocument(string openApiDocumentPath)
{
var descriptionForHuman = OAIDocument.Info?.Description.CleanupXMLString() is string d && !string.IsNullOrEmpty(d) ? d : $"Description for {OAIDocument.Info?.Title.CleanupXMLString()}";
var manifestInfo = ExtractInfoFromDocument(OAIDocument.Info);
return new PluginManifestDocument
{
SchemaVersion = "v1",
NameForHuman = OAIDocument.Info?.Title.CleanupXMLString(),
NameForModel = OAIDocument.Info?.Title.CleanupXMLString(),
DescriptionForHuman = descriptionForHuman,
DescriptionForModel = manifestInfo.DescriptionForModel ?? descriptionForHuman,
Auth = new V1AnonymousAuth(),
Api = new Api()
{
Type = ApiType.openapi,
URL = openApiDocumentPath
},
ContactEmail = manifestInfo.ContactEmail,
LogoUrl = manifestInfo.LogoUrl,
LegalInfoUrl = manifestInfo.LegalUrl,
};
}

private PluginManifestDocument GetManifestDocument(string openApiDocumentPath)
{
var (runtimes, functions) = GetRuntimesAndFunctionsFromTree(TreeNode, openApiDocumentPath);
var descriptionForHuman = OAIDocument.Info?.Description.CleanupXMLString() is string d && !string.IsNullOrEmpty(d) ? d : $"Description for {OAIDocument.Info?.Title.CleanupXMLString()}";
var descriptionForModel = descriptionForHuman;
string? legalUrl = null;
string? logoUrl = null;
string? privacyUrl = null;
if (OAIDocument.Info is not null)
{
if (OAIDocument.Info.Extensions.TryGetValue(OpenApiDescriptionForModelExtension.Name, out var descriptionExtension) &&
descriptionExtension is OpenApiDescriptionForModelExtension extension &&
!string.IsNullOrEmpty(extension.Description))
descriptionForModel = extension.Description.CleanupXMLString();
if (OAIDocument.Info.Extensions.TryGetValue(OpenApiLegalInfoUrlExtension.Name, out var legalExtension) && legalExtension is OpenApiLegalInfoUrlExtension legal)
legalUrl = legal.Legal;
if (OAIDocument.Info.Extensions.TryGetValue(OpenApiLogoExtension.Name, out var logoExtension) && logoExtension is OpenApiLogoExtension logo)
logoUrl = logo.Url;
if (OAIDocument.Info.Extensions.TryGetValue(OpenApiPrivacyPolicyUrlExtension.Name, out var privacyExtension) && privacyExtension is OpenApiPrivacyPolicyUrlExtension privacy)
privacyUrl = privacy.Privacy;
}
var manifestInfo = ExtractInfoFromDocument(OAIDocument.Info);
return new PluginManifestDocument
{
SchemaVersion = "v2",
NameForHuman = OAIDocument.Info?.Title.CleanupXMLString(),
// TODO name for model ???
DescriptionForHuman = descriptionForHuman,
DescriptionForModel = descriptionForModel,
ContactEmail = OAIDocument.Info?.Contact?.Email,
DescriptionForModel = manifestInfo.DescriptionForModel ?? descriptionForHuman,
ContactEmail = manifestInfo.ContactEmail,
Namespace = Configuration.ClientClassName,
LogoUrl = logoUrl,
LegalInfoUrl = legalUrl,
PrivacyPolicyUrl = privacyUrl,
LogoUrl = manifestInfo.LogoUrl,
LegalInfoUrl = manifestInfo.LegalUrl,
PrivacyPolicyUrl = manifestInfo.PrivacyUrl,
Runtimes = [.. runtimes
.GroupBy(static x => x, _openAPIRuntimeComparer)
.Select(static x =>
Expand All @@ -127,7 +145,40 @@ descriptionExtension is OpenApiDescriptionForModelExtension extension &&
Functions = [.. functions.OrderBy(static x => x.Name, StringComparer.OrdinalIgnoreCase)]
};
}
private (OpenApiRuntime[], Function[]) GetRuntimesAndFunctionsFromTree(OpenApiUrlTreeNode currentNode, string openApiDocumentPath)

private static OpenApiManifestInfo ExtractInfoFromDocument(OpenApiInfo? openApiInfo)
{
var manifestInfo = new OpenApiManifestInfo();

if (openApiInfo is null)
return manifestInfo;

string? descriptionForModel = null;
string? legalUrl = null;
string? logoUrl = null;
string? privacyUrl = null;
string contactEmail = string.IsNullOrEmpty(openApiInfo.Contact?.Email)
? DefaultContactEmail
: openApiInfo.Contact.Email;

if (openApiInfo.Extensions.TryGetValue(OpenApiDescriptionForModelExtension.Name, out var descriptionExtension) &&
descriptionExtension is OpenApiDescriptionForModelExtension extension &&
!string.IsNullOrEmpty(extension.Description))
descriptionForModel = extension.Description.CleanupXMLString();
if (openApiInfo.Extensions.TryGetValue(OpenApiLegalInfoUrlExtension.Name, out var legalExtension) && legalExtension is OpenApiLegalInfoUrlExtension legal)
legalUrl = legal.Legal;
if (openApiInfo.Extensions.TryGetValue(OpenApiLogoExtension.Name, out var logoExtension) && logoExtension is OpenApiLogoExtension logo)
logoUrl = logo.Url;
if (openApiInfo.Extensions.TryGetValue(OpenApiPrivacyPolicyUrlExtension.Name, out var privacyExtension) && privacyExtension is OpenApiPrivacyPolicyUrlExtension privacy)
privacyUrl = privacy.Privacy;

return new OpenApiManifestInfo(descriptionForModel, legalUrl, logoUrl, privacyUrl, contactEmail);

}
private const string DefaultContactName = "publisher-name";
private const string DefaultContactEmail = "[email protected]";
private sealed record OpenApiManifestInfo(string? DescriptionForModel = null, string? LegalUrl = null, string? LogoUrl = null, string? PrivacyUrl = null, string ContactEmail = DefaultContactEmail);
private static (OpenApiRuntime[], Function[]) GetRuntimesAndFunctionsFromTree(OpenApiUrlTreeNode currentNode, string openApiDocumentPath)
{
var runtimes = new List<OpenApiRuntime>();
var functions = new List<Function>();
Expand Down
Loading

0 comments on commit 6a07f54

Please sign in to comment.