CloudKit Bad Request on Users Record Update with Web Services API - cloudkit

I added a custom field to the default Users record type in CloudKit, and I'm trying to push a value to that new field.
Here's how my request is structured in Node JS:
var query = {
operations :[{
operationType: 'forceUpdate',
record:{
recordType: 'Users',
fields:{
myCustomField: { value: 'stuff' }
},
recordName: '_abc123'
}
}]
}
I'm getting this response from CloudKit:
records: [{
recordName: '_abc123',
reason: 'invalid id string, id=_abc123',
serverErrorCode: 'BAD_REQUEST'
}]
If I put that same custom field on another, custom Record Type (like if I make my own User (without the "s") type) that also has myCustomField on it, the update works fine. So there must be something special I have to do to update the system Users type.
Does anyone know how to update a field on a Users record with the web services API?

Related

Add a new field for already existing resource using PUT

I implemented google auth in my NextJS app. The idea is: user makes some progress working with my web app, I store this progress in local storage as an array. If he decides to register I receive the session back, then I send PUT request to db to update the document by inserting a new field (array) from local storage.
I implemented GET request that returns registered user data by email and it works. The question is, how to insert a new field using PUT method? In my case this field is array and calls progress. I'm not sure if I should use update.
This is the record from my mongodb:
_id: 63cc85641624a77f17ca5f29
name: "John P"
email: "john.p#gmail.com"
image: "https://lh3.googleusercontent.com/a/AEdFTp7xzF4eYtyhTgRxmgP4vYdCqDa6zW…"
emailVerified: null
I want to add a new field: progress: ['some data']
This is my PUT request:
case 'PUT':
const updateData = await fetch(`${baseUrl}/updateOne`, {
...fetchOptions,
body: JSON.stringify({
...fetchBody,
filter: { email: email },
update: {} <---------------!!!!!
}),
})
const updateDataJson = await updateData.json()
res.status(200).json(updateDataJson.documents)
break
If you want to update your document using updateOne, and add a progress key, you can use:
filter: { email: email },
update: {$set: {progress: arr}}

How should I store multiple nested arrays, populated using Mongoose populate(), in the cache using React Query?

Apologies if this is basic but I'm struggling to get my head around how to set this up.
I'm using MongoDB/Mongoose for my backend which returns a user object with nested arrays:
{
username: {
type: String,
unique: true
},
name: String,
avatar: String,
recommendations: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Media' }],
watchlist: [{
media: { type: mongoose.Schema.Types.ObjectId, ref: 'Media' },
date_added: Date,
}],
}
If a user visits their watchlist or recommendations page, the nested array gets populated, using mongoose populate(), with the referenced recommendations/watchlist items that they've added.
On the frontend I'm using React Query to handle the data returned from the server. Currently visiting either of the pages returns the whole user object, if I were to cache the entire object using the query key ['user'] the nested array not being populated will be stored as an array of reference id's. Instead I was thinking of maybe trying to update the nested arrays using setQueryData, however this doesn't work if the page is refreshed:
function useWatchlist() {
const { user } = useAuth()
const queryClient = useQueryClient()
const result = useQuery({
queryKey: ['user'],
queryFn: () =>
axios.get(`${baseUrl}/${user.profile_id}/watchlist`).then(response => response.data)
},
{onSuccess: (watchlist) => {
queryClient.setQueryData(['user'], oldUser => {
oldUser.watchlist === watchlist
})
}
})
return {...result, profile: result.data }
}
Should the recommendation/watchlist arrays instead be stored separately using different query keys - ['watchlist']/['recommendations'] or should I attempt to keep the user object structure being returned from the backend?
I would say that yes, you should store them separately. Yet, using relative keys (e.g. ['user', 'watchlist'] and ['user', 'recommendations']) as explained here under Structure:
Structure your Query Keys from most generic to most specific, with as many levels of granularity as you see fit in between
So, you can invalidate them both when the user is refetched.
When I store data such as the "watch list", which only changes when the user changes it, I put a staleTime: Infinity and use setQueryData in the onSuccess of the relevant mutation (when a user updates his watch list).
For the "recommendation list", it's different story, as it would be constantly changing by some logic in the backend. So, I would use invalidateQuery whenever the 'user' key is fetched (or expire the cache, if you update the list each certain interval), and populate it again, on the onSuccess for that query.

How to write JOIN in graphQL or get result from multiple types - AWS App sync iOS

I am using AWS AppSync for a chat app in one of the my applications. We are able to do setup and basic query successfully.
In one of the case I need to write a customized GraphQL query so that I can have additional data using reference of one type from another. For example, I can have allMessageGroup from a user and also allMessages from a particular group.
Now I want to add the last message in the group and its sender with the list of all message group just like what's app home page.
But I am not able to understand how make JOIN or write such query which give mixed results based on Conversation/Message/User types/table.
Platform:iOS
Language: Swift
For detail below is my Schema and API/Query I am using
Schema
type Conversation {
conversation_cover_pic: String
conversation_type: String!
createdAt: String
id: ID!
messages(after: String, first: Int): MessageConnection
name: String!
privacy: String
}
type Message {
author: User
content: String!
conversationId: ID!
createdAt: String
id: ID!
recipient: User
sender: String
}
type MessageConnection {
messages: [Message]
nextToken: String
}
Query
query getUserConversationConnectionThroughUser($after: String, $first: Int)
{
me
{
id
__typename
conversations(first: $first, after: $after)
{
__typename
nextToken
userConversations
{
__typename
userId
conversationId
associated
{
__typename
userId
}
conversation
{
__typename
id
name
privacy
messages
{
__typename
id
conversationId
content
createdAt
sender
isSent
}
}
}
}
}
}
It sounds like you need multiple requests to one or more datasources to fulfill this graphQL query. In this case, you can use AppSync's pipeline resolver feature.
With pipeline resolvers, you can create multiple functions, each of which can use the results of the previous function and query a database. These functions run in an order you specify.
An example of something you could do with a pipeline resolver:
One function will query the chat group database
A second function will use the results of the chat group to fetch messages
Consolidate all the results into one graphQL response containing group information and messages
Here is the documentation for pipeline resolvers: https://docs.aws.amazon.com/appsync/latest/devguide/pipeline-resolvers.html

sailsjs one-way associations confusion

I am having a hard time wrapping my head around associations with sailsjs.
I have 2 models
Services
attributes: {
status: {
defaultsTo: 'inactive'
},
userId:{
model: 'users',
via: 'id',
},
},
Users
attributes: {
email: {
type: 'string',
required: true,
unique: true
},
password: {
type: 'string'
}
},
So, a service is tied to a user (matching the id of the user).
I used to do a call like http://localhost:1337/Services?userId=userId
Now I would like to transition to associations using the above model attributes.
This works by calling the ID of the service just fine (it includes the users data as well), however if all i have is the user, how could I get the service
Doing the same call (http://localhost:1337/Services?userId=userId) returns and empty object.
Am I forced to actually have a one-to-one or one-to-many association? I don't understand why I can no longer use the userId field (stored in the DB) to do queries once I start using associations. I guess I am looking for the best of both worlds here.
EDIT:
Let me try make this more clear. Before trying to do associations, I could call this URL (using blueprint)
http://localhost:1337/Services?userId=userId
The Services model used to look like this
attributes: {
status: {
defaultsTo: 'inactive'
},
userId:{
type: 'string',
required: true,
},
},
Then when a user is created, a service for that user is created with the userId matching the ID in the Users table.
Now I would like to implement associations using the above model scheme.
However, because (my best guess) the userId field of the service is mapped to the Users model, I am unable to search for a Server using the userId field that is stored.
I hope that makes sense? In another words, tryin to call
http://localhost:1337/Services?userId=userId
returns nothing when using associations but does return a value when I don't use associations

Rest API get resource id by field

What is a correct rest way of getting a resource ID by a field, for example a name. Take a look at the following operations:
GET /users/mike-thomas
GET /users/rick-astley
I don't want to use these operations at my API end, instead I want to write an API operation that will get me the ID when submitting a field (name in the case of users) for example:
GET /users/id-by-field
Submitted data:
{
"fullName": "Mike Thomas"
}
Return data:
{
"data": {
"id": "123456789012345678901234"
}
}
What you want is known as an algorithmic URL where the parameters for the algorithm are passed as URL parameters:
GET /users?name="Mike Thomas"
Advantages are that you are using the "root" resource (users) and the search parameters are easily extended without having to change anything in the routing. For example:
GET /users?text="Mike"&year=1962&gender=M
where text would be searched for in more than just the name.
The resultant data would be a list of users and could return more than the identification of those users. Unless fullName uniquely identifies users, that is what you need to allow for anyway. And of course the list could contain a single user if the parameters uniquely identified that user.
{
users: [
{
id: "123456789012345678901234",
fullName: "Mike Thomas",
dateJoined: 19620228
}
, {
id: "234567890123456789012345"
fullName: "Rick Astley",
dateJoined: 19620227
}
]
}