Skip to content

X / Twitter api: Binary data upload is not possible in generated client #7394

@WolfgangHG

Description

@WolfgangHG

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

Csharp

Describe the bug

I initially asked this in #7247, but now I created a stripped down version of the X OpenAPI spec and a sample project, which contains the generated client (and batch files to create the client).

I am not sure whether this a Kiota problem or a bug on the X side.

For the /2/media/upload endpoint (see doc at https://docs.x.com/x-api/media/upload-media), Kiota creates classes MediaUploadRequestOneShot, MediaUploadRequestOneShot_media, MediaPayloadBinary and MediaPayloadByte, but the latter don't have any properties.
Here, it is not possible to upload a file.

The /2/media/upload/{id}/append endpoint (doc at https://docs.x.com/x-api/media/append-media-upload) creates MediaUploadAppendRequest and MediaUploadAppendRequestMember1, and the latter has a property Media of type byte[]. That's fine. The request contains the binary data in Base64 format.

Here is the relevant part of the definition:

First the component MediaUploadRequestOneShot used in /2/media/upload (that does not work):

"MediaUploadRequestOneShot" : {
  "type" : "object",
  "required" : [
    "media",
    "media_category"
  ],
  "properties" : {
    "additional_owners" : {
      "type" : "array",
      "items" : {
        "$ref" : "#/components/schemas/UserId"
      }
    },
    "media" : {
      "anyOf" : [
        {
          "$ref" : "#/components/schemas/MediaPayloadBinary"
        },
        {
          "$ref" : "#/components/schemas/MediaPayloadByte"
        }
      ]
    },
    ...
    "shared" : {
      "type" : "boolean",
      "description" : "Whether this media is shared or not.",
      "default" : false
    }
  },
  "additionalProperties" : false
},

This results in those classes (also stripped down):

  public partial class MediaUploadRequestOneShot : IParsable
  {
    public List<string> AdditionalOwners { get; set; }
    public MediaUploadRequestOneShot.MediaUploadRequestOneShot_media Media { get; set; }
    public bool? Shared { get; set; }

    public partial class MediaUploadRequestOneShot_media : IComposedTypeWrapper, IParsable
    {
      public MediaPayloadBinary MediaPayloadBinary { get; set; }

      public MediaPayloadByte MediaPayloadByte { get; set; }
    }
  }

  public partial class MediaPayloadBinary : IAdditionalDataHolder, IParsable
  {
    //No properties
  }

  public partial class MediaPayloadByte : IAdditionalDataHolder, IParsable
  {
    //No properties
  }

Now the second class of /2/media/upload/{id}/append endpoint (that has a property to set binary data and perform the upload):

"MediaUploadAppendRequest" : {
  "anyOf" : [
    {
      "type" : "object",
      "required" : [
        "media",
        "segment_index"
      ],
      "properties" : {
        "media" : {
          "$ref" : "#/components/schemas/MediaPayloadBinary"
        },
        "segment_index" : {
          "$ref" : "#/components/schemas/MediaSegments"
        }
      }
    },
    {
      "type" : "object",
      "required" : [
        "media",
        "segment_index"
      ],
      "properties" : {
        "media" : {
          "$ref" : "#/components/schemas/MediaPayloadByte"
        },
        "segment_index" : {
          "$ref" : "#/components/schemas/MediaSegments"
        }
      }
    }
  ]
},

The resulting classes:

public partial class MediaUploadAppendRequest : IComposedTypeWrapper, IParsable
{
    public MediaUploadAppendRequestMember1 MediaUploadAppendRequestMember1 { get; set; }
    public MediaUploadAppendRequestMember2 MediaUploadAppendRequestMember2 { get; set; }
}


public partial class MediaUploadAppendRequestMember1 : IAdditionalDataHolder, IParsable
{
    public byte[] Media { get; set; }
    public global::KiotaBinaryTest.Client.Models.MediaSegments SegmentIndex { get; set; }
}

public partial class MediaUploadAppendRequestMember2 : IAdditionalDataHolder, IParsable
{
    public byte[] Media { get; set; }
    public global::KiotaBinaryTest.Client.Models.MediaSegments SegmentIndex { get; set; }
}

Expected behavior

MediaUploadRequestOneShot should have a property Media of type byte[]

How to reproduce

Take attached sample, run "generate_kiota_client.bat" to create the client.
KiotaBinaryTest.zip

In "Form1.cs" constructor, there is a piece of code that shows how to uploads using the two endpoints:

XClient xClient = new XClient(null);

//This endpoint works:
MediaUploadAppendRequest muar = new MediaUploadAppendRequest();
muar.MediaUploadAppendRequestMember1 = new MediaUploadAppendRequestMember1();
muar.MediaUploadAppendRequestMember1.SegmentIndex = new MediaSegments();
muar.MediaUploadAppendRequestMember1.SegmentIndex.Integer = 1;

byte[] data = new byte[1000];
muar.MediaUploadAppendRequestMember1.Media = data;

MediaUploadAppendResponse? uploadAppendReponse = xClient.Two.Media.Upload["123"].Append.PostAsync(muar).GetAwaiter().GetResult();


//This endpoint lacks the binary data:
MediaUploadRequestOneShot mediaUpload = new MediaUploadRequestOneShot();
mediaUpload.MediaCategory = MediaCategoryOneShot.Tweet_image;
      
mediaUpload.Media = new MediaUploadRequestOneShot.MediaUploadRequestOneShot_media(); ;
//Here I would expect binary data:
mediaUpload.Media.MediaPayloadBinary = new MediaPayloadBinary();


MediaUploadResponse? uploadResponse = xClient.Two.Media.Upload.PostAsync(mediaUpload).GetAwaiter().GetResult();

Open API description file

openapi.json

Kiota Version

1.30.0

Latest Kiota version known to work for scenario above?(Not required)

No response

Known Workarounds

See #7247 (comment) - I could modify the generated class to contain a byte[] property

Configuration

No response

Other information

When creating the client, Kiota prints some warnings - they might be related:

warn: Kiota.Builder.KiotaBuilder[534942631]
      OpenAPI warning: #/ - The schema MediaUploadAppendRequest is a polymorphic type but does not define a discriminator. This will result in serialization errors.
warn: Kiota.Builder.KiotaBuilder[534942631]
      OpenAPI warning: #/components/schemas/MediaSegments/oneOf/1 - The format integer is not supported by Kiota for the type String and the string type will be used.
warn: Kiota.Builder.KiotaBuilder[1871951166]
      Discriminator MediaPayloadBinary is not inherited from MediaPayloadBinary.
warn: Kiota.Builder.KiotaBuilder[1871951166]
      Discriminator MediaPayloadByte is not inherited from MediaPayloadByte.
Generation completed successfully

Metadata

Metadata

Assignees

No one assigned

    Labels

    CsharpPull requests that update .net codestatus:waiting-for-triageAn issue that is yet to be reviewed or assignedtype:bugA broken experience

    Type

    No type

    Projects

    Status

    Needs Triage 🔍

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions