Skip to content

Commit 878dd08

Browse files
authored
Merge pull request #2121 from microsoft/fix/immutable-reference
fix/immutable reference
2 parents de9d979 + e0aba68 commit 878dd08

38 files changed

+279
-309
lines changed

src/Microsoft.OpenApi/Interfaces/IOpenApiReferenceHolder.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,6 @@ public interface IOpenApiReferenceHolder : IOpenApiSerializable
3434
/// <summary>
3535
/// Reference object.
3636
/// </summary>
37-
OpenApiReference Reference { get; set; }
37+
OpenApiReference Reference { get; init; }
3838
}
3939
}
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//TODO remove this if we ever remove the netstandard2.0 target
2+
#if !NET5_0_OR_GREATER
3+
namespace System.Runtime.CompilerServices {
4+
using System.ComponentModel;
5+
/// <summary>
6+
/// Reserved to be used by the compiler for tracking metadata.
7+
/// This class should not be used by developers in source code.
8+
/// </summary>
9+
[EditorBrowsable(EditorBrowsableState.Never)]
10+
internal static class IsExternalInit {
11+
}
12+
}
13+
#endif

src/Microsoft.OpenApi/Models/OpenApiDocument.cs

+7
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ namespace Microsoft.OpenApi.Models
2626
/// </summary>
2727
public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible, IOpenApiAnnotatable
2828
{
29+
/// <summary>
30+
/// Register components in the document to the workspace
31+
/// </summary>
32+
public void RegisterComponents()
33+
{
34+
Workspace?.RegisterComponents(this);
35+
}
2936
/// <summary>
3037
/// Related workspace containing components that are referenced in a document
3138
/// </summary>

src/Microsoft.OpenApi/Models/OpenApiMediaType.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public void SerializeAsV3(IOpenApiWriter writer)
8888
private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version,
8989
Action<IOpenApiWriter, IOpenApiSerializable> callback)
9090
{
91-
Utils.CheckArgumentNull(writer);;
91+
Utils.CheckArgumentNull(writer);
9292

9393
writer.WriteStartObject();
9494

src/Microsoft.OpenApi/Models/OpenApiReference.cs

+26-12
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44
using System;
55
using Microsoft.OpenApi.Extensions;
66
using Microsoft.OpenApi.Interfaces;
7+
using Microsoft.OpenApi.Models.Interfaces;
78
using Microsoft.OpenApi.Writers;
89

910
namespace Microsoft.OpenApi.Models
1011
{
1112
/// <summary>
1213
/// A simple object to allow referencing other components in the specification, internally and externally.
1314
/// </summary>
14-
public class OpenApiReference : IOpenApiSerializable
15+
public class OpenApiReference : IOpenApiSerializable, IOpenApiDescribedElement, IOpenApiSummarizedElement
1516
{
1617
/// <summary>
1718
/// A short summary which by default SHOULD override that of the referenced component.
@@ -32,13 +33,13 @@ public class OpenApiReference : IOpenApiSerializable
3233
/// 1. a absolute/relative file path, for example: ../commons/pet.json
3334
/// 2. a Url, for example: http://localhost/pet.json
3435
/// </summary>
35-
public string ExternalResource { get; set; }
36+
public string ExternalResource { get; init; }
3637

3738
/// <summary>
3839
/// The element type referenced.
3940
/// </summary>
4041
/// <remarks>This must be present if <see cref="ExternalResource"/> is not present.</remarks>
41-
public ReferenceType? Type { get; set; }
42+
public ReferenceType? Type { get; init; }
4243

4344
/// <summary>
4445
/// The identifier of the reusable component of one particular ReferenceType.
@@ -47,7 +48,7 @@ public class OpenApiReference : IOpenApiSerializable
4748
/// If ExternalResource is not present, this is the name of the component without the reference type name.
4849
/// For example, if the reference is '#/components/schemas/componentName', the Id is 'componentName'.
4950
/// </summary>
50-
public string Id { get; set; }
51+
public string Id { get; init; }
5152

5253
/// <summary>
5354
/// Gets a flag indicating whether this reference is an external reference.
@@ -62,12 +63,13 @@ public class OpenApiReference : IOpenApiSerializable
6263
/// <summary>
6364
/// Gets a flag indicating whether a file is a valid OpenAPI document or a fragment
6465
/// </summary>
65-
public bool IsFragment = false;
66+
public bool IsFragment { get; init; }
6667

68+
private OpenApiDocument hostDocument;
6769
/// <summary>
6870
/// The OpenApiDocument that is hosting the OpenApiReference instance. This is used to enable dereferencing the reference.
6971
/// </summary>
70-
public OpenApiDocument HostDocument { get; set; }
72+
public OpenApiDocument HostDocument { get => hostDocument; init => hostDocument = value; }
7173

7274
/// <summary>
7375
/// Gets the full reference string for v3.0.
@@ -145,12 +147,13 @@ public OpenApiReference() { }
145147
/// </summary>
146148
public OpenApiReference(OpenApiReference reference)
147149
{
148-
Summary = reference?.Summary;
149-
Description = reference?.Description;
150-
ExternalResource = reference?.ExternalResource;
151-
Type = reference?.Type;
152-
Id = reference?.Id;
153-
HostDocument = reference?.HostDocument;
150+
Utils.CheckArgumentNull(reference);
151+
Summary = reference.Summary;
152+
Description = reference.Description;
153+
ExternalResource = reference.ExternalResource;
154+
Type = reference.Type;
155+
Id = reference.Id;
156+
HostDocument = reference.HostDocument;
154157
}
155158

156159
/// <summary>
@@ -276,5 +279,16 @@ private string GetReferenceTypeNameAsV2(ReferenceType type)
276279
// to indicate that the reference is not pointing to any object.
277280
};
278281
}
282+
283+
/// <summary>
284+
/// Sets the host document after deserialization or before serialization.
285+
/// This method is internal on purpose to avoid consumers mutating the host document.
286+
/// </summary>
287+
/// <param name="currentDocument">Host document to set if none is present</param>
288+
internal void EnsureHostDocumentIsSet(OpenApiDocument currentDocument)
289+
{
290+
Utils.CheckArgumentNull(currentDocument);
291+
hostDocument ??= currentDocument;
292+
}
279293
}
280294
}

src/Microsoft.OpenApi/Models/References/BaseOpenApiReferenceHolder.cs

+5-18
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,12 @@ namespace Microsoft.OpenApi.Models.References;
1010
/// <typeparam name="V">The interface type for the model.</typeparam>
1111
public abstract class BaseOpenApiReferenceHolder<T, V> : IOpenApiReferenceHolder<T, V> where T : class, IOpenApiReferenceable, V where V : IOpenApiSerializable
1212
{
13-
/// <summary>
14-
/// The resolved target object.
15-
/// </summary>
16-
protected T _target;
1713
/// <inheritdoc/>
1814
public virtual T Target
1915
{
2016
get
2117
{
22-
_target ??= Reference.HostDocument?.ResolveReferenceTo<T>(Reference);
23-
return _target;
18+
return Reference.HostDocument?.ResolveReferenceTo<T>(Reference);
2419
}
2520
}
2621
/// <summary>
@@ -34,16 +29,6 @@ protected BaseOpenApiReferenceHolder(BaseOpenApiReferenceHolder<T, V> source)
3429
//no need to copy summary and description as if they are not overridden, they will be fetched from the target
3530
//if they are, the reference copy will handle it
3631
}
37-
private protected BaseOpenApiReferenceHolder(T target, string referenceId, ReferenceType referenceType)
38-
{
39-
_target = target;
40-
41-
Reference = new OpenApiReference()
42-
{
43-
Id = referenceId,
44-
Type = referenceType,
45-
};
46-
}
4732
/// <summary>
4833
/// Constructor initializing the reference object.
4934
/// </summary>
@@ -55,9 +40,11 @@ private protected BaseOpenApiReferenceHolder(T target, string referenceId, Refer
5540
/// 1. a absolute/relative file path, for example: ../commons/pet.json
5641
/// 2. a Url, for example: http://localhost/pet.json
5742
/// </param>
58-
protected BaseOpenApiReferenceHolder(string referenceId, OpenApiDocument hostDocument, ReferenceType referenceType, string externalResource = null)
43+
protected BaseOpenApiReferenceHolder(string referenceId, OpenApiDocument hostDocument, ReferenceType referenceType, string externalResource)
5944
{
6045
Utils.CheckArgumentNullOrEmpty(referenceId);
46+
// we're not checking for null hostDocument as it's optional and can be set via additional methods by a walker
47+
// this way object initialization of a whole document is supported
6148

6249
Reference = new OpenApiReference()
6350
{
@@ -70,7 +57,7 @@ protected BaseOpenApiReferenceHolder(string referenceId, OpenApiDocument hostDoc
7057
/// <inheritdoc/>
7158
public bool UnresolvedReference { get => Reference is null || Target is null; }
7259
/// <inheritdoc/>
73-
public OpenApiReference Reference { get; set; }
60+
public OpenApiReference Reference { get; init; }
7461
/// <inheritdoc/>
7562
public abstract V CopyReferenceAsTargetElementWithOverrides(V source);
7663
/// <inheritdoc/>

src/Microsoft.OpenApi/Models/References/OpenApiCallbackReference.cs

+1-5
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class OpenApiCallbackReference : BaseOpenApiReferenceHolder<OpenApiCallba
2525
/// 1. an absolute/relative file path, for example: ../commons/pet.json
2626
/// 2. a Url, for example: http://localhost/pet.json
2727
/// </param>
28-
public OpenApiCallbackReference(string referenceId, OpenApiDocument hostDocument, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Callback, externalResource)
28+
public OpenApiCallbackReference(string referenceId, OpenApiDocument hostDocument = null, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Callback, externalResource)
2929
{
3030
}
3131
/// <summary>
@@ -37,10 +37,6 @@ private OpenApiCallbackReference(OpenApiCallbackReference callback):base(callbac
3737

3838
}
3939

40-
internal OpenApiCallbackReference(OpenApiCallback target, string referenceId):base(target, referenceId, ReferenceType.Callback)
41-
{
42-
}
43-
4440
/// <inheritdoc/>
4541
public Dictionary<RuntimeExpression, IOpenApiPathItem> PathItems { get => Target?.PathItems; }
4642

src/Microsoft.OpenApi/Models/References/OpenApiExampleReference.cs

+1-5
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class OpenApiExampleReference : BaseOpenApiReferenceHolder<OpenApiExample
2525
/// 1. a absolute/relative file path, for example: ../commons/pet.json
2626
/// 2. a Url, for example: http://localhost/pet.json
2727
/// </param>
28-
public OpenApiExampleReference(string referenceId, OpenApiDocument hostDocument, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Example, externalResource)
28+
public OpenApiExampleReference(string referenceId, OpenApiDocument hostDocument = null, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Example, externalResource)
2929
{
3030
}
3131
/// <summary>
@@ -36,10 +36,6 @@ private OpenApiExampleReference(OpenApiExampleReference example):base(example)
3636
{
3737
}
3838

39-
internal OpenApiExampleReference(OpenApiExample target, string referenceId):base(target, referenceId, ReferenceType.Example)
40-
{
41-
}
42-
4339
/// <inheritdoc/>
4440
public string Description
4541
{

src/Microsoft.OpenApi/Models/References/OpenApiHeaderReference.cs

+1-5
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public class OpenApiHeaderReference : BaseOpenApiReferenceHolder<OpenApiHeader,
2424
/// 1. a absolute/relative file path, for example: ../commons/pet.json
2525
/// 2. a Url, for example: http://localhost/pet.json
2626
/// </param>
27-
public OpenApiHeaderReference(string referenceId, OpenApiDocument hostDocument, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Header, externalResource)
27+
public OpenApiHeaderReference(string referenceId, OpenApiDocument hostDocument = null, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Header, externalResource)
2828
{
2929
}
3030

@@ -36,10 +36,6 @@ private OpenApiHeaderReference(OpenApiHeaderReference header):base(header)
3636
{
3737
}
3838

39-
internal OpenApiHeaderReference(OpenApiHeader target, string referenceId):base(target, referenceId, ReferenceType.Header)
40-
{
41-
}
42-
4339
/// <inheritdoc/>
4440
public string Description
4541
{

src/Microsoft.OpenApi/Models/References/OpenApiLinkReference.cs

+1-4
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public class OpenApiLinkReference : BaseOpenApiReferenceHolder<OpenApiLink, IOpe
2424
/// 1. a absolute/relative file path, for example: ../commons/pet.json
2525
/// 2. a Url, for example: http://localhost/pet.json
2626
/// </param>
27-
public OpenApiLinkReference(string referenceId, OpenApiDocument hostDocument, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Link, externalResource)
27+
public OpenApiLinkReference(string referenceId, OpenApiDocument hostDocument = null, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Link, externalResource)
2828
{
2929
}
3030
/// <summary>
@@ -34,9 +34,6 @@ public OpenApiLinkReference(string referenceId, OpenApiDocument hostDocument, st
3434
private OpenApiLinkReference(OpenApiLinkReference reference):base(reference)
3535
{
3636
}
37-
internal OpenApiLinkReference(OpenApiLink target, string referenceId):base(target, referenceId, ReferenceType.Link)
38-
{
39-
}
4037

4138
/// <inheritdoc/>
4239
public string Description

src/Microsoft.OpenApi/Models/References/OpenApiParameterReference.cs

+1-5
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public class OpenApiParameterReference : BaseOpenApiReferenceHolder<OpenApiParam
2323
/// 1. a absolute/relative file path, for example: ../commons/pet.json
2424
/// 2. a Url, for example: http://localhost/pet.json
2525
/// </param>
26-
public OpenApiParameterReference(string referenceId, OpenApiDocument hostDocument, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Parameter, externalResource)
26+
public OpenApiParameterReference(string referenceId, OpenApiDocument hostDocument = null, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Parameter, externalResource)
2727
{
2828
}
2929

@@ -35,10 +35,6 @@ private OpenApiParameterReference(OpenApiParameterReference parameter):base(para
3535
{
3636
}
3737

38-
internal OpenApiParameterReference(OpenApiParameter target, string referenceId):base(target, referenceId, ReferenceType.Parameter)
39-
{
40-
}
41-
4238
/// <inheritdoc/>
4339
public string Name { get => Target?.Name; }
4440

src/Microsoft.OpenApi/Models/References/OpenApiPathItemReference.cs

+1-5
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public class OpenApiPathItemReference : BaseOpenApiReferenceHolder<OpenApiPathIt
2424
/// 1. a absolute/relative file path, for example: ../commons/pet.json
2525
/// 2. a Url, for example: http://localhost/pet.json
2626
/// </param>
27-
public OpenApiPathItemReference(string referenceId, OpenApiDocument hostDocument, string externalResource = null): base(referenceId, hostDocument, ReferenceType.PathItem, externalResource)
27+
public OpenApiPathItemReference(string referenceId, OpenApiDocument hostDocument = null, string externalResource = null): base(referenceId, hostDocument, ReferenceType.PathItem, externalResource)
2828
{
2929
}
3030

@@ -37,10 +37,6 @@ private OpenApiPathItemReference(OpenApiPathItemReference pathItem):base(pathIte
3737

3838
}
3939

40-
internal OpenApiPathItemReference(OpenApiPathItem target, string referenceId):base(target, referenceId, ReferenceType.PathItem)
41-
{
42-
}
43-
4440
/// <inheritdoc/>
4541
public string Summary
4642
{

src/Microsoft.OpenApi/Models/References/OpenApiRequestBodyReference.cs

+1-4
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class OpenApiRequestBodyReference : BaseOpenApiReferenceHolder<OpenApiReq
2525
/// 1. a absolute/relative file path, for example: ../commons/pet.json
2626
/// 2. a Url, for example: http://localhost/pet.json
2727
/// </param>
28-
public OpenApiRequestBodyReference(string referenceId, OpenApiDocument hostDocument, string externalResource = null):base(referenceId, hostDocument, ReferenceType.RequestBody, externalResource)
28+
public OpenApiRequestBodyReference(string referenceId, OpenApiDocument hostDocument = null, string externalResource = null):base(referenceId, hostDocument, ReferenceType.RequestBody, externalResource)
2929
{
3030
}
3131
/// <summary>
@@ -35,9 +35,6 @@ public OpenApiRequestBodyReference(string referenceId, OpenApiDocument hostDocum
3535
private OpenApiRequestBodyReference(OpenApiRequestBodyReference openApiRequestBodyReference):base(openApiRequestBodyReference)
3636
{
3737

38-
}
39-
internal OpenApiRequestBodyReference(OpenApiRequestBody target, string referenceId):base(target, referenceId, ReferenceType.RequestBody)
40-
{
4138
}
4239

4340
/// <inheritdoc/>

src/Microsoft.OpenApi/Models/References/OpenApiResponseReference.cs

+1-5
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public class OpenApiResponseReference : BaseOpenApiReferenceHolder<OpenApiRespon
2323
/// 1. a absolute/relative file path, for example: ../commons/pet.json
2424
/// 2. a Url, for example: http://localhost/pet.json
2525
/// </param>
26-
public OpenApiResponseReference(string referenceId, OpenApiDocument hostDocument, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Response, externalResource)
26+
public OpenApiResponseReference(string referenceId, OpenApiDocument hostDocument = null, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Response, externalResource)
2727
{
2828
}
2929
/// <summary>
@@ -35,10 +35,6 @@ private OpenApiResponseReference(OpenApiResponseReference openApiResponseReferen
3535

3636
}
3737

38-
internal OpenApiResponseReference(OpenApiResponse target, string referenceId):base(target, referenceId, ReferenceType.Response)
39-
{
40-
}
41-
4238
/// <inheritdoc/>
4339
public string Description
4440
{

src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs

+1-5
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class OpenApiSchemaReference : BaseOpenApiReferenceHolder<OpenApiSchema,
2525
/// 1. a absolute/relative file path, for example: ../commons/pet.json
2626
/// 2. a Url, for example: http://localhost/pet.json
2727
/// </param>
28-
public OpenApiSchemaReference(string referenceId, OpenApiDocument hostDocument, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Schema, externalResource)
28+
public OpenApiSchemaReference(string referenceId, OpenApiDocument hostDocument = null, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Schema, externalResource)
2929
{
3030
}
3131
/// <summary>
@@ -36,10 +36,6 @@ private OpenApiSchemaReference(OpenApiSchemaReference schema):base(schema)
3636
{
3737
}
3838

39-
internal OpenApiSchemaReference(OpenApiSchema target, string referenceId):base(target, referenceId, ReferenceType.Schema)
40-
{
41-
}
42-
4339
/// <inheritdoc/>
4440
public string Description
4541
{

src/Microsoft.OpenApi/Models/References/OpenApiSecuritySchemeReference.cs

+1-4
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public class OpenApiSecuritySchemeReference : BaseOpenApiReferenceHolder<OpenApi
1919
/// <param name="referenceId">The reference Id.</param>
2020
/// <param name="hostDocument">The host OpenAPI document.</param>
2121
/// <param name="externalResource">The externally referenced file.</param>
22-
public OpenApiSecuritySchemeReference(string referenceId, OpenApiDocument hostDocument, string externalResource = null):base(referenceId, hostDocument, ReferenceType.SecurityScheme, externalResource)
22+
public OpenApiSecuritySchemeReference(string referenceId, OpenApiDocument hostDocument = null, string externalResource = null):base(referenceId, hostDocument, ReferenceType.SecurityScheme, externalResource)
2323
{
2424
}
2525
/// <summary>
@@ -29,9 +29,6 @@ public OpenApiSecuritySchemeReference(string referenceId, OpenApiDocument hostDo
2929
private OpenApiSecuritySchemeReference(OpenApiSecuritySchemeReference openApiSecuritySchemeReference):base(openApiSecuritySchemeReference)
3030
{
3131

32-
}
33-
internal OpenApiSecuritySchemeReference(OpenApiSecurityScheme target, string referenceId):base(target, referenceId, ReferenceType.SecurityScheme)
34-
{
3532
}
3633

3734
/// <inheritdoc/>

0 commit comments

Comments
 (0)