microsoft / kiota-typescript

TypeScript libraries for Kiota-generated API clients.
https://aka.ms/kiota/docs
MIT License
32 stars 25 forks source link

Kiota doesn't serialize request body with embedded dictionary object , eg { someObject: {key: value}} #1194

Closed rners01 closed 1 month ago

rners01 commented 1 month ago

What are you generating using Kiota, clients or plugins?

API Client/SDK

In what context or format are you using Kiota?

Windows executable

Client library/SDK language

TypeScript

Describe the bug

Im trying to send request data utilizing typescript Kiota client, but the client serializes request body with embedded dictionary object defaulting it to empty object = {}.

My consoled payload before sending through Kiota client:

{
  "myDictionaryObject": {
    "someValue1":  "someKey1",
    "someValue2":  "someKey2",
    },
  "description": "An example text",
}

Payload form Network tab - after Kiota serialization

See myDictionaryObject: {}

{
  "myDictionaryObject": {},
  "description": "An example text",
}

Potential problem

I think the problem could be with the way I'm describing my dictionary object, in terms of TS I expect here Record<string, string> my swagger specification snippet which I use to build kiota client

{
  "myDictionaryObject": {
    "description": "The job input schema.",
    "type": "object",
    "additionalProperties": {
      "type": "string"
    }
  },
}

Expected behavior

send full data with embedded object key, value pairs

How to reproduce

send embedded data eg. {key1: {key2: value1, key3: value2 }}

    "@microsoft/kiota-abstractions": "1.0.0-preview.53",
    "@microsoft/kiota-http-fetchlibrary": "1.0.0-preview.52",
    "@microsoft/kiota-serialization-json": "1.0.0-preview.53",

Kiota Version

1.14.0+fc4b39c65d89f7bfc8c7f1813c197e95e206da09

Configuration

Snippets

my swagger specification snippet which I use to build kiota client

{
  "myDictionaryObject": {
    "description": "The job input schema.",
    "type": "object",
    "additionalProperties": {
      "type": "string"
    }
  },
}

entry file refine wrapper

<Refine
    dataProvider={{
      default: dataProvider(apiUrl),
      inferenceEndpoints: {
        getList: ...,
        getOne: ...,
        update: async ({ id, variables }) => {
            try {
              console.log(variables) --> here the structure is correct
              const data = await apiClient.api.admin.endpoints // HERE apiClient is kiota-typescript generated client
                .byName(id)
                .patch(variables)

              return { data }
            } catch (error) {
              throw getParsedError(error)
            }
          }
        }
        notificationProvider={useNotificationProvider}
        routerProvider={routerBindings}
        authProvider={createAuthProvider()}
        i18nProvider={i18nProvider}
        resources={getResources(apiClient)}
        options={{
          disableTelemetry: true,
          projectId: 'some-project-id',
          syncWithLocation: true,
          useNewQueryKeys: true,
          warnWhenUnsavedChanges: true,
          }}>
          <Routes>... </Routes>
  </Refine>

My client setup

export const getAuthenticatedApiClient = (): ApiClient => {
  const requestAdapter = new FetchRequestAdapter(
    new BaseBearerTokenAuthenticationProvider({
      getAuthorizationToken() {
        const token = sessionStorage.getItem(TOKEN_KEY) as string

        return Promise.resolve(token)
      },
      getAllowedHostsValidator: () => new AllowedHostsValidator(new Set([apiUrl])),
    }),
  )
  requestAdapter.baseUrl = apiUrl
  return createApiClient(requestAdapter)
  }

  export function createApiClient(requestAdapter: RequestAdapter) {
    registerDefaultSerializer(JsonSerializationWriterFactory);
    registerDefaultDeserializer(JsonParseNodeFactory);
    if (requestAdapter.baseUrl === undefined || requestAdapter.baseUrl === "") {
        requestAdapter.baseUrl = "https://my-url.com";
    }
    const pathParameters: Record<string, unknown> = {
        "baseurl": requestAdapter.baseUrl,
    };
    return apiClientProxifier<ApiClient>(requestAdapter, pathParameters, ApiClientNavigationMetadata, undefined);
}
baywet commented 1 month ago

Hi thank you for reporting this issue can you share the interface that was generated by kiota with us please?

rners01 commented 1 month ago

Thanks for quick reply @baywet, here is generated interface

export interface UpdateEndpointRequest_my_object extends AdditionalDataHolder, Parsable {
    /**
     * Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well.
     */
    additionalData?: Record<string, unknown>;
}
baywet commented 1 month ago

Thanks for sharing this, have you tried setting those extra properties in additional data, so something like

{
  "myDictionaryObject": {
    "additionalData": {
      "someValue1":  "someKey1",
      "someValue2":  "someKey2",
      },
    },
  "description": "An example text",
}
rners01 commented 1 month ago

They are dynamic keys and values, thats why it's

interface Directory {
  [key: string]: string
}
baywet commented 1 month ago

I'm not sure I understand the last comment? The additional data property is a free form dictionary for properties that are not described in the schema, hence my suggestion to use it.

rners01 commented 1 month ago

Hey @baywet, I'm not sure I understand. I don't know about the names/values of dynamic dictionary, so what do you mean by this example with specific keys "someValue1, ..."?

{
  "myDictionaryObject": {
    "additionalData": {
      "someValue1":  "someKey1",
      "someValue2":  "someKey2",
      },
    },
  "description": "An example text",
}

In my case dynamic props are described like so

{
  "myDictionaryObject": {
    "description": "The job input schema.",
    "type": "object",
    "additionalProperties": {
      "type": "string"
    }
  },
}
rners01 commented 1 month ago

Hey @baywet, can you please explain your proposal?

andrueastman commented 1 month ago

@rners01 the additionalProperties will be held in the additionalData dictionary as key value pairs. What @baywet is suggesting is setting the values in additionalData property rather than the root of the myDictionaryObject s that's where the Kiota serializer will look for additional properties not described in the reference schema.

microsoft-github-policy-service[bot] commented 1 month ago

This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 4 days. It will be closed if no further activity occurs within 3 days of this comment.