Skip to content

Commit a182f44

Browse files
committed
fix: makes reference of holder immutable
Signed-off-by: Vincent Biret <[email protected]>
1 parent 92877e0 commit a182f44

File tree

7 files changed

+25
-23
lines changed

7 files changed

+25
-23
lines changed

src/Microsoft.OpenApi/Interfaces/IOpenApiReferenceHolder.cs

Lines changed: 1 addition & 1 deletion
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
}

src/Microsoft.OpenApi/Models/OpenApiReference.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,11 @@ public class OpenApiReference : IOpenApiSerializable, IOpenApiDescribedElement,
7878
/// </summary>
7979
public bool IsFragment { get; init; }
8080

81+
private OpenApiDocument openApiDocument;
8182
/// <summary>
8283
/// The OpenApiDocument that is hosting the OpenApiReference instance. This is used to enable dereferencing the reference.
8384
/// </summary>
84-
public OpenApiDocument HostDocument { get; init; }
85+
public OpenApiDocument HostDocument { get => openApiDocument; init => openApiDocument = value; }
8586

8687
/// <summary>
8788
/// Gets the full reference string for v3.0.
@@ -291,5 +292,16 @@ private string GetReferenceTypeNameAsV2(ReferenceType type)
291292
// to indicate that the reference is not pointing to any object.
292293
};
293294
}
295+
296+
/// <summary>
297+
/// Sets the host document after deserialization or before serialization.
298+
/// This method is internal on purpose to avoid consumers mutating the host document.
299+
/// </summary>
300+
/// <param name="currentDocument">Host document to set if none is present</param>
301+
internal void EnsureHostDocumentIsSet(OpenApiDocument currentDocument)
302+
{
303+
Utils.CheckArgumentNull(currentDocument);
304+
openApiDocument ??= currentDocument;
305+
}
294306
}
295307
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ protected BaseOpenApiReferenceHolder(string referenceId, OpenApiDocument hostDoc
7171
/// <inheritdoc/>
7272
public bool UnresolvedReference { get => Reference is null || Target is null; }
7373
/// <inheritdoc/>
74-
public OpenApiReference Reference { get; set; }
74+
public OpenApiReference Reference { get; init; }
7575
/// <inheritdoc/>
7676
public abstract V CopyReferenceAsTargetElementWithOverrides(V source);
7777
/// <inheritdoc/>

src/Microsoft.OpenApi/Services/ReferenceHostDocumentSetter.cs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,7 @@ public ReferenceHostDocumentSetter(OpenApiDocument currentDocument)
2121
/// <inheritdoc/>
2222
public override void Visit(IOpenApiReferenceHolder referenceHolder)
2323
{
24-
if (referenceHolder.Reference != null)
25-
{
26-
referenceHolder.Reference = new OpenApiReference(referenceHolder.Reference)
27-
{
28-
HostDocument = _currentDocument,
29-
};
30-
}
24+
referenceHolder.Reference?.EnsureHostDocumentIsSet(_currentDocument);
3125
}
3226
}
3327
}

test/Microsoft.OpenApi.Readers.Tests/ReferenceService/TryLoadReferenceV2Tests.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,7 @@ public async Task LoadResponseAndSchemaReference()
118118
}
119119
};
120120

121-
var schemaReference = (OpenApiSchemaReference)expected.Content["application/json"].Schema;
122-
schemaReference.Reference = new OpenApiReference(schemaReference.Reference)
123-
{
124-
HostDocument = result.Document,
125-
};
121+
((OpenApiSchemaReference)expected.Content["application/json"].Schema).Reference.EnsureHostDocumentIsSet(result.Document);
126122
var actual = reference.Target;
127123

128124
// Assert

test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,11 +1046,11 @@ public async Task ParseModifiedPetStoreDocumentWithTagAndSecurityShouldSucceed()
10461046
}
10471047
};
10481048

1049-
tagReference1.Reference = new OpenApiReference(tagReference1.Reference) {HostDocument = expected };
1050-
tagReference2.Reference = new OpenApiReference(tagReference2.Reference) {HostDocument = expected };
1051-
petSchemaReference.Reference = new OpenApiReference(petSchemaReference.Reference) {HostDocument = expected };
1052-
newPetSchemaReference.Reference = new OpenApiReference(newPetSchemaReference.Reference) {HostDocument = expected };
1053-
errorModelSchemaReference.Reference = new OpenApiReference(errorModelSchemaReference.Reference) {HostDocument = expected };
1049+
tagReference1.Reference.EnsureHostDocumentIsSet(expected);
1050+
tagReference2.Reference.EnsureHostDocumentIsSet(expected);
1051+
petSchemaReference.Reference.EnsureHostDocumentIsSet(expected);
1052+
newPetSchemaReference.Reference.EnsureHostDocumentIsSet(expected);
1053+
errorModelSchemaReference.Reference.EnsureHostDocumentIsSet(expected);
10541054

10551055
actual.Document.Should().BeEquivalentTo(expected, options => options
10561056
.IgnoringCyclicReferences()
@@ -1284,7 +1284,7 @@ public async Task ParseDocWithRefsUsingProxyReferencesSucceeds()
12841284
var outputDoc = (await doc.SerializeAsYamlAsync(OpenApiSpecVersion.OpenApi3_0)).MakeLineBreaksEnvironmentNeutral();
12851285
var expectedParam = expected.Paths["/pets"].Operations[OperationType.Get].Parameters[0];
12861286
var expectedParamReference = Assert.IsType<OpenApiParameterReference>(expectedParam);
1287-
expectedParamReference.Reference = new OpenApiReference(expectedParamReference.Reference) {HostDocument = doc};
1287+
expectedParamReference.Reference.EnsureHostDocumentIsSet(doc);
12881288

12891289
var actualParamReference = Assert.IsType<OpenApiParameterReference>(actualParam);
12901290

test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,8 @@ namespace Microsoft.OpenApi.Interfaces
224224
}
225225
public interface IOpenApiReferenceHolder : Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiSerializable
226226
{
227-
Microsoft.OpenApi.Models.OpenApiReference Reference { get; set; }
228227
bool UnresolvedReference { get; }
228+
Microsoft.OpenApi.Models.OpenApiReference Reference { get; init; }
229229
}
230230
public interface IOpenApiReferenceHolder<out T, V> : Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiReferenceHolder, Microsoft.OpenApi.Interfaces.IOpenApiSerializable
231231
where out T : Microsoft.OpenApi.Interfaces.IOpenApiReferenceable, V
@@ -1243,9 +1243,9 @@ namespace Microsoft.OpenApi.Models.References
12431243
protected readonly T _target;
12441244
protected BaseOpenApiReferenceHolder(Microsoft.OpenApi.Models.References.BaseOpenApiReferenceHolder<T, V> source) { }
12451245
protected BaseOpenApiReferenceHolder(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument, Microsoft.OpenApi.Models.ReferenceType referenceType, string externalResource = null) { }
1246-
public Microsoft.OpenApi.Models.OpenApiReference Reference { get; set; }
12471246
public virtual T Target { get; }
12481247
public bool UnresolvedReference { get; }
1248+
public Microsoft.OpenApi.Models.OpenApiReference Reference { get; init; }
12491249
public abstract V CopyReferenceAsTargetElementWithOverrides(V source);
12501250
public virtual void SerializeAsV2(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { }
12511251
public virtual void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { }

0 commit comments

Comments
 (0)