Kiota AnyOf Int/String Validation Failure: A Deep Dive

by Luna Greco 55 views

Hey guys,

Let's dive into an interesting issue encountered while using Kiota to generate a TypeScript SDK. Specifically, it revolves around the anyOf schema construct when dealing with integer and string types. This article will walk you through the problem, the expected behavior, how to reproduce it, and some potential workarounds. So, buckle up and let's get started!

Understanding the Issue

The core problem lies in how Kiota handles the anyOf keyword in OpenAPI schemas, especially when it involves a mix of integer and string types. In the reported scenario, the ssh_key_identifier parameter is defined as either an integer (representing an SSH key ID) or a string (representing an SSH key fingerprint). However, Kiota seems to be defaulting to generating only the integer type, which prevents developers from accessing queries that require the string (fingerprint) format.

The OpenAPI Schema Snippet

To better illustrate the problem, let's take a look at the relevant snippet from the OpenAPI schema:

ssh_key_identifier:
 in: path
 name: ssh_key_identifier
 required: true
 description: Either the ID or the fingerprint of an existing SSH key.
 schema:
 anyOf:
 - $ref: '#/components/schemas/ssh_key_id' # This is a number
 - $ref: '#/components/schemas/ssh_key_fingerprint' # This is a string
 example: 123456

ssh_key_id:
 type: integer
 description: >-
 A unique identification number for this key. Can be used to embed a
 specific SSH key into a Droplet.
 readOnly: true
 example: 512189

ssh_key_fingerprint:
 type: string
 description: >-
 A unique identifier that differentiates this key from other keys using
 a format that SSH recognizes. The fingerprint is created when the key is
 added to your account.
 readOnly: true
 example: 3b:16:bf:e4:8b:00:8b:b8:59:8c:a9:d3:f0:19:45:fa

As you can see, the ssh_key_identifier can be either an ssh_key_id (integer) or an ssh_key_fingerprint (string). The expectation is that Kiota should generate a TypeScript type that reflects this anyOf condition, allowing both types to be used.

The Bug Manifestation

Instead of generating a function signature like this:

ssh_key_identifier(body: number | string)

Kiota is generating:

ssh_key_identifier(body: number)

This means that developers are unable to utilize the string (fingerprint) queries, effectively limiting the functionality of the generated SDK.

Expected Behavior

The ideal behavior here is for Kiota to correctly interpret the anyOf schema and generate a TypeScript type that supports both the integer and string types. This would typically involve creating a union type (number | string) in the generated code. By doing so, developers can seamlessly use either the SSH key ID or the fingerprint when making API calls, providing the flexibility intended by the OpenAPI schema.

Why is This Important?

Supporting anyOf with mixed types is crucial for several reasons:

  1. Flexibility: It allows APIs to accept different types of identifiers or parameters, making them more versatile.
  2. Adherence to OpenAPI Standards: The anyOf keyword is a standard part of the OpenAPI specification, and tools like Kiota should handle it correctly.
  3. Developer Experience: Generating accurate types improves the developer experience by reducing the need for manual type coercion or workarounds.

How to Reproduce the Issue

Reproducing this issue is straightforward. All you need is an OpenAPI schema that uses anyOf to define a parameter as either an integer or a string. Here’s a step-by-step guide:

  1. Create an OpenAPI Schema: Use the YAML snippet provided earlier, or create a similar schema that defines a parameter with anyOf including integer and string types.
  2. Generate the SDK with Kiota: Use the Kiota CLI to generate a TypeScript SDK from the schema. For example:
    kiota generate --openapi <your-schema>.yaml --language TypeScript --output <output-directory>
    
  3. Inspect the Generated Code: Look at the generated function signature for the parameter defined with anyOf. You’ll likely find that it only includes the integer type, and not the union type (number | string).

By following these steps, you can easily reproduce the issue and confirm that Kiota is not correctly handling the anyOf construct in this scenario.

Known Workarounds and Potential Solutions

As of the current version (1.27.0) of Kiota, there isn’t a straightforward workaround for this issue. However, here are a few potential approaches and considerations:

1. Manual Type Assertion

One temporary workaround is to use manual type assertions in your code. This involves casting the value to the correct type when calling the generated function. For example:

const identifier: number | string =