Open pshao25 opened 1 year ago
Decision:
ContentType
of concenience method should be Azure.Core.ContentType
This how we map a media type string to category in M4:
export function knownMediaType(mediaType: string) {
const mt = parseMediaType(mediaType);
if (mt) {
if ((mt.subtype === json || mt.suffix === json) && (mt.type === application || mt.type === text)) {
return KnownMediaType.Json;
}
if ((mt.subtype === xml || mt.suffix === xml) && (mt.type === application || mt.type === text)) {
return KnownMediaType.Xml;
}
if (mt.type === "audio" || mt.type === "image" || mt.type === "video" || mt.subtype === "octet-stream") {
return KnownMediaType.Binary;
}
if (mt.type === application && mt.subtype === formEncoded) {
return KnownMediaType.Form;
}
if (mt.type === "multipart" && mt.subtype === "form-data") {
return KnownMediaType.Multipart;
}
if (mt.type === application) {
// at this point, an unrecognized application/* is considered a binary format
// since we don't have any other way of dealing with it.
return KnownMediaType.Binary;
}
if (mt.type === "text") {
return KnownMediaType.Text;
}
}
return KnownMediaType.Unknown;
}
export function parseMediaType(mediaType: string) {
if (mediaType) {
const parsed =
/(application|audio|font|example|image|message|model|multipart|text|video|x-(?:[0-9A-Za-z!#$%&'*+.^_`|~-]+))\/([0-9A-Za-z!#$%&'*.^_`|~-]+)\s*(?:\+([0-9A-Za-z!#$%&'*.^_`|~-]+))?\s*(?:;.\s*(\S*))?/g.exec(
mediaType,
);
if (parsed) {
return {
type: parsed[1],
subtype: parsed[2],
suffix: parsed[3],
parameter: parsed[4],
};
}
}
return undefined;
}
All the content type string with this pattern is valid:
/(application|audio|font|example|image|message|model|multipart|text|video|x-(?:[0-9A-Za-z!#$%&'*+.^_`|~-]+))\/([0-9A-Za-z!#$%&'*.^_`|~-]+)\s*(?:\+([0-9A-Za-z!#$%&'*.^_`|~-]+))?\s*(?:;.\s*(\S*))?/
And they will be categoried into one of Binary
, Form
, Json
, Multipart
, Text
, Xml
by above logic.
KnownMediaType.Json:
// Only one media type
public virtual Response PostParameters(Model parameter, CancellationToken cancellationToken = default)
{
Response response = PostParameters(parameter.ToRequestContent(), "application/json", context);
}
// More than one media type
public virtual Response PostParameters(Model parameter, ContentType contentType, CancellationToken cancellationToken = default)
{
Response response = PostParameters(parameter.ToRequestContent(), contentType, context);
}
KnownMediaType.Binary
// Only one media type
public virtual Response PostParameters(BinaryData parameter, CancellationToken cancellationToken = default)
{
Response response = PostParameters(parameter, "image/jpeg", context);
}
// More than one media type
public virtual Response PostParameters(BinaryData parameter, ContentType contentType, CancellationToken cancellationToken = default)
{
Response response = PostParameters(parameter, contentType, context);
}
KnownMediaType.Text
// Only one media type
public virtual Response PostParameters(string parameter, CancellationToken cancellationToken = default)
{
Response response = PostParameters(parameter, "text/plain", context);
}
// More than one media type
public virtual Response PostParameters(string parameter, ContentType contentType, CancellationToken cancellationToken = default)
{
Response response = PostParameters(parameter, contentType, context);
}
KnownMediaType.Xml (e.g. application/xml) We just don't support. KnownMediaType.Form (e.g. application/x-www-form-urlencoded) We just don't support. KnownMediaType.Multipart We just don't support.
No matter how many media types, only one protocol method and one request method
public virtual Response PostParameters(RequestContent content, ContentType contentType, RequestContext context = null)
{
using HttpMessage message = CreatePostParametersRequest(content, contentType, context);
}
internal HttpMessage CreatePostParametersRequest(RequestContent content, ContentType contentType, RequestContext context)
{
request.Headers.Add("content-type", contentType.ToString());
request.Content = content;
}
Invalid cases handling:
When the content types listed in the header don't have corresponding body types. For example, "application/json" should map to a model, but the paramter type is bytes | string
.
op postParameters(@body parameter: bytes | string, @header contentType: "application/json" | "image/jpeg"): void;
Same cases are "image/jpeg" is in contentType but bytes
is not in the type of parameter
.
When the content types contain "application/json", but more than one models are listed in parameter
. We don't which model to use.
op postParameters(@body parameter: Model1 | Model2, @header contentType: "application/json"): void;
In the overload method, more than one types of parameter
are listed. In the below example, the type of parameter
is Model | string
. More than one types. Therefore, we cannot describe it in one method.
@overload(postParameters)
op postParametersApplicationJson(parameter: Model | string, @header contentType: "text/plain" | "application/json" ): void;
When both json and xml exist, their signatures are the same, then how could we decide which content writer to use?
Corner cases handling:
@overload(postParameters)
op postParametersImage(@body parameter: bytes, @header contentType: "image/jpeg" | "image/png"): void;
@route("/serviceDriven/parameters") op postParameters(@body parameter: bytes | Thing, @header contentType: "application/json" | "image/jpeg" | "image/png"): void;
There is only one `postParametersImage`. No `postParametersJson`.
2. Overload method has `convenientAPI` while base operation not, or the other way around. Same situation to `protocolAPI`.
@overload(postParameters) @convenientAPI(true) op postParametersImage(@body parameter: bytes, @header contentType: "image/jpeg" | "image/png"): void;
@route("/serviceDriven/parameters") @convenientAPI(false) op postParameters(@body parameter: bytes | Thing, @header contentType: "application/json" | "image/jpeg" | "image/png"): void;
The decision of Java is:
@overload
to define multiple content scenario. There will be linter to prevent the definition which defines both body type as union and header type as union. User should define overload if they define body type as union and header type as union.@overload
will generate corresponding convenience method with the mapping it defines, no matter whether there is @convenientAPI
or whether the mapping is reasonable.Decision on Feb 23rd:
We don't take multiple content type as a special case. Any cases with overload or shared route come up, we will just discuss overload or shared route.
Sample swagger: We want 4 content types: "text/plain", "application/json", "image/jpeg", "image/png"
Cadl: Example in cadl-ranch: https://github.com/Azure/cadl-ranch/pull/201
We expect user writes this. We could have internal mapping like M4 (how it maps needs to confirm with Tim):
Generated SDK:
Above examples are under the condition if there are more than 1 content types for the specific type. If there is only one content type, we omit
ContentType
parameter. For example, this case we will generate:Fine tune mapping:
The generated SDK becomes:
Remaining issues:
ContentType
of concenience method should beAzure.Core.ContentType
? Or same as HLC to be a enum? If the answer is latter, then there are twoContentType
for two convenience methods respectively. We need to rename them. (Java uses string ContentType)Response<BinaryData>
.