-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: allow non-component references #132
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,7 @@ | |
using System.Collections.Generic; | ||
using LEGO.AsyncAPI.Exceptions; | ||
using LEGO.AsyncAPI.Models.Interfaces; | ||
using LEGO.AsyncAPI.Writers; | ||
Check warning on line 9 in src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs GitHub Actions / build (macos-latest)
|
||
using LEGO.AsyncAPI.Services; | ||
|
||
/// <summary> | ||
|
@@ -150,7 +150,7 @@ | |
return this.ResolveReference(reference) as T; | ||
} | ||
|
||
public IAsyncApiReferenceable ResolveReference(AsyncApiReference reference) | ||
internal IAsyncApiReferenceable ResolveReference(AsyncApiReference reference) | ||
{ | ||
if (reference == null) | ||
{ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,14 @@ | |
/// </summary> | ||
public class AsyncApiReference : IAsyncApiSerializable | ||
{ | ||
/// <summary> | ||
/// External resource in the reference. | ||
/// It maybe: | ||
/// 1. a absolute/relative file path, for example: ../commons/pet.json | ||
/// 2. a Url, for example: http://localhost/pet.json | ||
/// </summary> | ||
public string ExternalResource { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the element type referenced. | ||
/// </summary> | ||
|
@@ -27,17 +35,37 @@ | |
public AsyncApiDocument HostDocument { get; set; } = null; | ||
|
||
/// <summary> | ||
/// Gets the full reference string for v2.3. | ||
/// Gets a flag indicating whether a file is a valid OpenAPI document or a fragment | ||
/// </summary> | ||
public bool IsFragment { get; set; } = false; | ||
|
||
/// <summary> | ||
/// Gets a flag indicating whether this reference is an external reference. | ||
/// </summary> | ||
public bool IsExternal => this.ExternalResource != null; | ||
|
||
/// <summary> | ||
/// Gets the full reference string for v2. | ||
/// </summary> | ||
public string Reference | ||
{ | ||
get | ||
{ | ||
if (this.IsExternal) | ||
{ | ||
return this.GetExternalReferenceV2(); | ||
} | ||
|
||
if (!this.Type.HasValue) | ||
{ | ||
throw new ArgumentNullException(nameof(this.Type)); | ||
} | ||
|
||
//if (this.Type == ReferenceType.SecurityScheme) | ||
Check warning on line 64 in src/LEGO.AsyncAPI/Models/AsyncApiReference.cs GitHub Actions / build (windows-latest)
Check warning on line 64 in src/LEGO.AsyncAPI/Models/AsyncApiReference.cs GitHub Actions / build (macos-latest)
|
||
//{ | ||
Check warning on line 65 in src/LEGO.AsyncAPI/Models/AsyncApiReference.cs GitHub Actions / build (windows-latest)
Check warning on line 65 in src/LEGO.AsyncAPI/Models/AsyncApiReference.cs GitHub Actions / build (macos-latest)
|
||
// return this.Id; | ||
//} | ||
Check warning on line 67 in src/LEGO.AsyncAPI/Models/AsyncApiReference.cs GitHub Actions / build (windows-latest)
Check warning on line 67 in src/LEGO.AsyncAPI/Models/AsyncApiReference.cs GitHub Actions / build (windows-latest)
Check warning on line 67 in src/LEGO.AsyncAPI/Models/AsyncApiReference.cs GitHub Actions / build (macos-latest)
Check warning on line 67 in src/LEGO.AsyncAPI/Models/AsyncApiReference.cs GitHub Actions / build (macos-latest)
|
||
|
||
return "#/components/" + this.Type.GetDisplayName() + "/" + this.Id; | ||
} | ||
} | ||
|
@@ -67,6 +95,21 @@ | |
writer.WriteEndObject(); | ||
} | ||
|
||
private string GetExternalReferenceV2() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure I understand what intention is with this method. If, its external why does it start pointing an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a case, where the reference is a full asyncapidocument, and we are pointing at a 'real' reference there. |
||
{ | ||
if (this.Id != null) | ||
{ | ||
if (this.IsFragment) | ||
{ | ||
return this.ExternalResource + "#" + this.Id; | ||
} | ||
|
||
return this.ExternalResource + "#/components/" + this.Type.GetDisplayName() + "/" + this.Id; | ||
} | ||
|
||
return this.ExternalResource; | ||
} | ||
|
||
public void Write(IAsyncApiWriter writer) | ||
{ | ||
this.SerializeV2(writer); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -154,7 +154,7 @@ public override void Visit(AsyncApiSchema schema) | |
this.ResolveMap(schema.Properties); | ||
} | ||
|
||
private void ResolveObject<T>(T entity, Action<T> assign) where T : class, IAsyncApiReferenceable | ||
private void ResolveObject<T>(T entity, Action<T> assign) where T : class, IAsyncApiReferenceable, new() | ||
{ | ||
if (entity == null) | ||
{ | ||
|
@@ -184,7 +184,7 @@ private void ResolveObject<T>(T entity, Action<T> assign) where T : class, IAsyn | |
} | ||
} | ||
|
||
private void ResolveMap<T>(IDictionary<string, T> map) where T : class, IAsyncApiReferenceable | ||
private void ResolveMap<T>(IDictionary<string, T> map) where T : class, IAsyncApiReferenceable, new() | ||
{ | ||
if (map == null) | ||
{ | ||
|
@@ -201,8 +201,17 @@ private void ResolveMap<T>(IDictionary<string, T> map) where T : class, IAsyncAp | |
} | ||
} | ||
|
||
private T ResolveReference<T>(AsyncApiReference reference) where T : class, IAsyncApiReferenceable | ||
private T ResolveReference<T>(AsyncApiReference reference) where T : class, IAsyncApiReferenceable, new() | ||
{ | ||
if (reference.IsExternal) | ||
{ | ||
return new () | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So basically take the reference loader in the ctor, and use that if isExternal. Yeah |
||
{ | ||
UnresolvedReference = true, | ||
Reference = reference, | ||
}; | ||
} | ||
|
||
try | ||
{ | ||
return this.currentDocument.ResolveReference(reference) as T; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably worth noting that I believe the spec also supports "relative" paths. So they could conceivably start with
.
and..
as well.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is already fully supported, gotta have to check for the case of having a fragment though.
Something like
./myjsonfile.json/fragment
that would simply fall under 'externalresource' and not
isFragment: true
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, just to clarify, something like a schema stored as a yaml file separately using cloudevents or some other schema would be an
externalResource
and also a fragment. But a reference to an entire AsyncAPI doc would simply be anexternalResource
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think fragments are, when a subset is referenced inside another Json file. (Which is not what I stated above - it should have isFragment:true).
So
jsonfile.json#/someLocation
would be external AND a fragmentLike
/whatever
is the same, but in the same host document.But I got dizzy from all of the RFCs involved.
I need to be determine 100% what "fragment" actually refers to.
I'll get back to you on that, once clarified, so we are 100% in line and on point regarding when, which is what.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After some investigating, ive come to the understanding that there are 3 types of references
./external-doc.json#/fragment
different file document. (relative path)/fragments/myFragment
same document.https://example.com/document.json#/fragment
different external file document.#/components/schemas/Pet
(we already support those)./external-doc.json
We can choose to support this (although it technically does not align with the spec.')and is a mix of
Json Reference
JSON Pointer
Uri
The main point is paragraph 3 and 4 of the Json Reference RFC
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ive pushed tests that should verify this behaviour.
So now it should be a matter of figuring out the Uri scheme, fetching the file/fragment accordingly, using the loader and insert values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, interesting, definitely adds some clarity.
Especially so that "'Full' document References" aren't in this RFC because I've seen them used a lot on AsyncAPI slack to allow sharing of resources between OpenAPI schemas, AsyncAPI schemas and schema registry.
Is it significant that all the examples are to .json files? Or can we allow for .yaml as well?
Similar context in terms of spec intention vs facilitating actual usage: I'm never sure with this stuff because I'm sure there's a similar situation with
servers
where the AsyncAPI spec doesn't actually specify its contents can be$ref
'd but I've seen a many members of the core AsyncAPI team themselves reffing the servers object in their config.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given the
IReferenceLoader
pattern on my PR we could actually just push this concern to the user. They can decide how they want to read when using external files. We could provide them with implementations for the canonical uses outlined. But if they want to do something extremely weird then they've got the interface to implement around their needs.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My thoughts exactly