tsl-apple-cloudkit icon indicating copy to clipboard operation
tsl-apple-cloudkit copied to clipboard

RECORD SAVE - CloudKit wouldn't accept a LIST of STRING as a field

Open inferutsers opened this issue 1 year ago • 11 comments

What type of issue is this?

Bug

What is incorrect, incomplete, or missing?

Hi, I am trying to save a record in which one of the fields is a list of strings. I am creating an array of RecordFields of type "STRING" and passing it as a RecordField of type "LIST"'s value. There are no errors thrown by the compiler. Problem is when I send the request, I get "BadRequestException: Unexpected input". From what I understand it should work.

What platforms does this issue apply to, if applicable?

No response

What did you expect to see?

Record saved successfully

How did you test this?

I ran the code using nextjs hosted on vps.

Can you link to a related release note, pull request, or page?

No response

Do you have anything more you want to share?

No response

inferutsers avatar Apr 18 '24 20:04 inferutsers

Hello, @inferutsers!

Thank you for the report. Please provide more information about the implementation. From what I understand you are using the wrong type as indicated by the server.

The CloudKit JS documentation contains a link to the correct type setting for each value type.

Maybe this already helps.

sophiebremer avatar Apr 20 '24 08:04 sophiebremer

Hello

This is the record I am trying to save: const record: RecordToCreate = { recordType: "TestType", fields: { "testList": { type: "LIST", value: listArray } };

listArray is:

const list = ["string1", "string2"]; const listArray: RecordField[] = list.map((e) => {return {type: "STRING", value: e} as RecordField});

And from apple's api I am getting: _ckErrorCode: 'BAD_REQUEST', _reason: 'BadRequestException: Unexpected input', _serverErrorCode: 'BAD_REQUEST'

If you need any more info I am happy to provide you with that.

inferutsers avatar Apr 20 '24 08:04 inferutsers

Thanks. Your code looks correct. I have to create a test case tomorrow to see, if I can reproduce the issue.

Just a hunch, but maybe CloudKit JS does the array mapping now. It is worth a try to assign the string array with an any-cast as the direct value:

const list = ["string1", "string2"];
const record: RecordToCreate = {
  recordType: "TestType",
  fields: { 
    "testList": { type: "LIST", value: list as any }
  }
};

sophiebremer avatar Apr 20 '24 09:04 sophiebremer

I tested your hunch but it doesn't seem to make a difference. Let's see if you can reproduce the issue tomorrow.

inferutsers avatar Apr 20 '24 09:04 inferutsers

Any new info? Still wondering whether its something on my end.

inferutsers avatar Apr 23 '24 14:04 inferutsers

Hello!

Sorry for the delay. I have issues with my CloudKit profiles and could not test yet.

I can only think of two other fields for failure:

  • The container might not be configured properly, either the types, or created for iOS app and not web app.

  • The encoding of the strings might be an issue.

If I get the profile issues solved, I will run some tests this weekend.

sophiebremer avatar Apr 23 '24 19:04 sophiebremer

Hello, there is the schema for the context:

RECORD TYPE TestType (
        "___createTime" TIMESTAMP,
        "___createdBy"  REFERENCE,
        "___etag"       STRING,
        "___modTime"    TIMESTAMP,
        "___modifiedBy" REFERENCE,
        "___recordID"   REFERENCE QUERYABLE,
        testList        LIST<STRING> QUERYABLE,
        GRANT WRITE TO "_creator",
        GRANT READ, CREATE, WRITE TO "_icloud",
        GRANT READ TO "_world"
    );

I don't really know what you mean by "created for iOS app and not web app". For my knowledge all containers are created through identifiers. Quoting Apple's documentation: "In CloudKit Dashboard, enable web services by creating either an API token or server-to-server key". There is a server-to-server key created and actively used to perform queries.

inferutsers avatar Apr 23 '24 22:04 inferutsers

Also, if that helps:

CloudKitJS CloudKit Database#saveRecords [
  {
    recordType: 'TestType',
    fields: {
      testList: [Object]
    }
  }
] {}
CloudKitJS RecordsBatchBuilder#createOrUpdate [
  {
    recordType: 'TestType',
    fields: {
      testList: [Object]
    }
  }
] {}
CloudKitJS RecordsBatchBuilder#create {
  recordType: 'TestType',
  fields: {
    testList: { type: 'LIST', value: [Array] }
  }
} {}
CloudKitJS RecordsBatchBuilder#commit
t [Error]: BadRequestException: Unexpected input

That's the output I am getting

inferutsers avatar Apr 23 '24 23:04 inferutsers

It is some years since I touched CloudKit stuff. I will contact support tomorrow, to get help with my access issue. Everything from my side is just pure speculation so far, as I do not get into my CloudKit dashboard.

I don't really know what you mean by "created for iOS app and not web app". For my knowledge all containers are created through identifiers. Quoting Apple's documentation: "In CloudKit Dashboard, enable web services by creating either an API token or server-to-server key". There is a server-to-server key created and actively used to perform queries.

I do not recall the details. Back then I made the mistake to mix iOS and web connections, as they expect different kind of data, iOS expects binary, web expects JSON.

That's the output I am getting

This shows at least, that the records work in the browser. Do you maybe use the wrong database? Your schema looks like records are supposed to go into the sharedCloudDatabase. Also check the environment. Both client and container have to run either in development or production.

sophiebremer avatar Apr 28 '24 15:04 sophiebremer

I've checked everything and it all seems fine. Any progress on your side?

inferutsers avatar May 02 '24 15:05 inferutsers

I am in contact with support and expect in the coming days some form of solution to access the dashboard again.

sophiebremer avatar May 05 '24 11:05 sophiebremer

Hello, any progress?

inferutsers avatar May 26 '24 10:05 inferutsers

Hello @inferutsers,

Sorry for the delay. I got finally access to all necessary parts of CloudKit. So here are my results:

  1. I was able to reproduce your issue.
  2. A step in the right direction was to remove all type information from the list object ment to be saved.
  3. I still got an error, but then it was a regular result with error property that complained about the list structure.
  4. Considering the fact that it became better after removing type properties, I tried a simple string array, which actually worked.

So the solution for your use case is:

const record: RecordToCreate = { recordType: "TestType", fields: {  "testList": list as any } };

Let me know if this is working for you as well. I will fix the typing asap.

sophiebremer avatar Jun 05 '24 20:06 sophiebremer