kiota icon indicating copy to clipboard operation
kiota copied to clipboard

Issue with Kiota or open api spec file?

Open jreidryuzaki opened this issue 1 year ago • 15 comments

I'm using the UPS Rating API open api spec file to generate a client. https://github.com/UPS-API/api-documentation/blob/main/Rating.json

"RateResponse": { "type": "object", "required": [ "Response", "RatedShipment" ], "properties": { "Response": { "$ref": "#/components/schemas/RateResponse_Response" }, "RatedShipment": { "oneOf": [ { "items": { "$ref": "#/components/schemas/RateResponse_RatedShipment" }, "type": "array" }, { "$ref": "#/components/schemas/RateResponse_RatedShipment" } ] } }, "xml": { "name": "RateResponse" }, "description": "Rate Response Container.", "maximum": 1 }

It's creating a class RateResponse with another class inside RatedShipment with two properties that are referencing itself instead of the actual $ref object.

Presumably, there needs to be an 's' at the end of the RatedShipments property to avoid this, is that correct?

jreidryuzaki avatar Jan 02 '24 18:01 jreidryuzaki

RatedShipment is a oneOf that is challenging to support in languages that do not have union types. In order to do this we create a wrapper class that has a property for each of the schemas in the oneOf.

 "RatedShipment": {
            "oneOf": [
              {
                "items": {
                  "$ref": "#/components/schemas/RateResponse_RatedShipment"
                },
                "type": "array"
              },
              {
                "$ref": "#/components/schemas/RateResponse_RatedShipment"
              }
            ]

The oneOf is for either a single RatedShipment instance or an array of RatedShipment. In the deserialized response object, one of the two properties will be set depending on whether there was one or multiple RatedShipments that are returned.

darrelmiller avatar Jan 02 '24 19:01 darrelmiller

The $ref is not being used in the wrapper class. It is referencing itself in the generated model. I have to change the namespace for each property because the class names are generated as the same.

image

jreidryuzaki avatar Jan 02 '24 19:01 jreidryuzaki

Ahh, I see what you are saying. That does seem to be a bug. Thanks for the follow up.

darrelmiller avatar Jan 02 '24 20:01 darrelmiller

What seems to be throwing the generation off the rails here is that component schema is:

  • a union type
  • matching the inline schema naming conventions
  • used for the endpoint it's the conventions matches

So kiota is having a hard time understanding which is what during the generation and mushing things together. So it's most likely a very narrow edge case here.

What would be interesting is to look at the CodeDOM before refiners are applied to see whether the defect comes from the core generation engine or whether it comes from the union type refiner.

@jreidryuzaki if I give you pointers, is this something you'd like to look into?

baywet avatar Jan 09 '24 21:01 baywet

@jreidryuzaki if I give you pointers, is this something you'd like to look into?

Absolutely.

jreidryuzaki avatar Jan 10 '24 01:01 jreidryuzaki

Great! You'll probably want to set a breakpoint here as this is where the union type is generated. https://github.com/microsoft/kiota/blob/0feecd579ee7c38bad8e5078053a77c2306cacaf/src/Kiota.Builder/KiotaBuilder.cs#L1816

And another one here as this is where they are converted to wrapper classes. https://github.com/microsoft/kiota/blob/0feecd579ee7c38bad8e5078053a77c2306cacaf/src/Kiota.Builder/Refiners/CommonLanguageRefiner.cs#L470C6-L470C6

Lastly, you'll want to update the description path/url here https://github.com/microsoft/kiota/blob/0feecd579ee7c38bad8e5078053a77c2306cacaf/.vscode/launch.json#L55 (if you're using VS instead of vscode, the parameters here should be easy to transcribe to launch parameters)

The thing to be on the lookout is when a class gets added, is it actually being added, or is another one being reused because it has the same name. Let us know if you need more information.

baywet avatar Jan 10 '24 15:01 baywet

In a case like this, are we expecting the type that implements IComposedTypeWrapper to have the suffix "Wrapper"? Or are we expecting the Targetnamespace to be different than the Targetnamespace of the IComposedTypeWrapper implementation?

jreidryuzaki avatar Jan 16 '24 15:01 jreidryuzaki

The naming conventions follow these guidelines:

  • Root reusable schema component: the last segment (split on dots) of the component schema name
  • inline schema for a property of a reusable schema: like previous appended with _propertyName suffix
  • inline schema root request body: path segment prefix + operation + RequestBody suffix (FooPostRequestBody)
  • inline property for an inline request body: same as above, with _PropertyName additional suffix (FooPostRequestBody_Property)
  • inline schema root response body: path segment prefix + operation + Response suffix (FooGetResponse)
  • inline property for an inline response body: same as above, with _PropertyName additional suffix (FooPostResponse_Property)

Let me know if you have further questions, thanks for looking into this.

baywet avatar Jan 16 '24 15:01 baywet

From what I can tell the naming conventions are getting followed properly.

I think what's going on is that there is no suffix on the component "RateResponse" and thus the "RatedShipment" property is causing the wrapper class to get named the same as the $ref object for the "RatedShipment" property.

I'm not sure what other solution there is here besides changing the UPS Rating spec file to either

  • include a suffix for the root component
  • change the name of the $ref class in the RatedShipment property

jreidryuzaki avatar Jan 17 '24 19:01 jreidryuzaki

Actually here is what I think is happening: The union type is defined inline for the property, so it's name will be "ParentType_PropertyName" (RateResponse_RatedShipment). But here we already have a component schema (therefore a class) with that same exact name, and it's kind of mushing things together.

You can probably see that by debugging this code section:

https://github.com/microsoft/kiota/blob/8f5d3b8fcb950bd6606c7ef04c71355a7de7c278/src/Kiota.Builder/Refiners/CommonLanguageRefiner.cs#L493

And if you pay attention to the other two blocks, they both have a guard close to add a "Wrapper" suffix when something already exists, but not this one. I think that just adding this guard clause to that block should do the trick. Would you mind looking into this further please?

baywet avatar Jan 19 '24 14:01 baywet

This block is never reached like you suggested. https://github.com/microsoft/kiota/blob/56c42d05528a8bf7bbd63ca70671e33d199eac7c/src/Kiota.Builder/Refiners/CommonLanguageRefiner.cs#L491-L500

This is the only block reached. The condition comparing class names is always false so no Wrapper suffix is added when it gets here. https://github.com/microsoft/kiota/blob/56c42d05528a8bf7bbd63ca70671e33d199eac7c/src/Kiota.Builder/Refiners/CommonLanguageRefiner.cs#L509-L513

Should it be hitting the middle block? If so, where should it be creating the namespaces at?

jreidryuzaki avatar Jan 24 '24 17:01 jreidryuzaki

yes, I'm suggesting to replicate it at line 492.

(I just paste the "copy as link" value in)

baywet avatar Jan 25 '24 12:01 baywet

I edited my comment with a different question. I still don't think we're on the same page, sorry to be confusing!

jreidryuzaki avatar Jan 25 '24 14:01 jreidryuzaki

Forked and narrowed it down here. For my specific case, I was able to get the guard clause to be true only for the RateResponse_RatedShipment. https://github.com/microsoft/kiota/commit/74ca63c626cb17d8821da21072a80be80e3de479 - you may have to change the output path in launchsettings but the url to the UPS Rating API should be current.

jreidryuzaki avatar Jan 26 '24 20:01 jreidryuzaki

Thanks for the continuous investigation on this one. Why don't you add a unit test case and submit a pull request for it? One of the things I can see from the approach here is that if you have multiple nested inline types, you'll get multiple _, so the solution should account for that.

baywet avatar Jan 29 '24 18:01 baywet