I'm trying to build a chat application in React Native where the listChats query is sorted by time but I have problems sorting with the new GraphQL transformer v2. How do I sort the model by time?
My chats model in the schema below...
type Chat #model {
id: ID!
content: String! #index(name: "chatsByDate", queryField: "chatsByDate", sortKeyFields: ["createdAt"])
createdAt: AWSDateTime!
file: String
userID: ID! #index(name: "byUser")
user: User #belongsTo(fields: ["userID"])
convoID: ID! #index(name: "byConvo")
convo: Convo #belongsTo(fields: ["convoID"])}
This is how I'm querying the data...
const fetchChats = async () => {
setLoading(true)
try {
const filter = {
convoID: {
eq: convoId
},
}
const chatsData = await API.graphql(graphqlOperation(listChats, {filter: filter}));
setChats(chatsData.data.listChats.items)
} catch (error) {
console.log(error)
} finally {
setLoading(false)
}
}
I'm creating a REST API using AWS CDK version 1.22 and I would like to document my API using CDK as well, but I do not see any documentation generated for my API after deployment.
I've dived into aws docs, cdk example, cdk reference but I could find concrete examples that help me understand how to do it.
Here is my code:
const app = new App();
const api = new APIStack(app, 'APIStack', { env }); // basic api gateway
// API Resources
const resourceProps: APIResourceProps = {
gateway: api.gateway,
}
// dummy endpoint with some HTTP methods
const siteResource = new APISiteStack(app, 'APISiteStack', {
env,
...resourceProps
});
const siteResourceDocs = new APISiteDocs(app, 'APISiteDocs', {
env,
...resourceProps,
});
// APISiteDocs is defined as follow:
class APISiteDocs extends Stack {
constructor(scope: Construct, id: string, props: APIResourceProps) {
super(scope, id, props);
new CfnDocumentationVersion(this, 'apiDocsVersion', {
restApiId: props.gateway.restApiId,
documentationVersion: config.app.name(`API-${config.gateway.api.version}`),
description: 'Spare-It API Documentation',
});
new CfnDocumentationPart(this, 'siteDocs', {
restApiId: props.gateway.restApiId,
location: {
type: 'RESOURCE',
method: '*',
path: APISiteStack.apiBasePath,
statusCode: '405',
},
properties: `
{
"status": "error",
"code": 405,
"message": "Method Not Allowed"
}
`,
});
}
}
Any help/hint is appreciated, Thanks.
I have tested with CDK 1.31 and it is possible to use the CDK's default deployment option and also add a document version to the stage. I have used the deployOptions.documentVersion in rest api definition to set the version identifier of the API documentation:
import * as cdk from '#aws-cdk/core';
import * as apigateway from "#aws-cdk/aws-apigateway";
import {CfnDocumentationPart, CfnDocumentationVersion} from "#aws-cdk/aws-apigateway";
export class CdkSftpStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const documentVersion = "v1";
// create the API
const api = new apigateway.RestApi(this, 'books-api', {
deploy: true,
deployOptions: {
documentationVersion: documentVersion
}
});
// create GET method on /books resource
const books = api.root.addResource('books');
books.addMethod('GET');
// // create documentation for GET method
new CfnDocumentationPart(this, 'doc-part1', {
location: {
type: 'METHOD',
method: 'GET',
path: books.path
},
properties: JSON.stringify({
"status": "successful",
"code": 200,
"message": "Get method was succcessful"
}),
restApiId: api.restApiId
});
new CfnDocumentationVersion(this, 'docVersion1', {
documentationVersion: documentVersion,
restApiId: api.restApiId,
description: 'this is a test of documentation'
});
}
}
From what I can gather, if you use the CDK's default deployment options which create stage and deployment on your behalf, it won't be possible to append the stage with a documentation version set.
Instead, the solution would be to set the RESTAPI's option object to deploy:false and define the stage and deployment manually.
stack.ts code
import * as cdk from '#aws-cdk/core';
import * as apigateway from '#aws-cdk/aws-apigateway';
import { Stage, Deployment, CfnDocumentationPart, CfnDocumentationVersion, CfnDeployment } from '#aws-cdk/aws-apigateway';
export class StackoverflowHowToDocumentRestApiUsingAwsCdkStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// create the API, need to not rely on CFN's automatic deployment because we need to
// make our own deployment to set the documentation we create
const api = new apigateway.RestApi(this, 'books-api',{
deploy: false
});
// create GET method on /books resource
const books = api.root.addResource('books');
books.addMethod('GET');
// // create documentation for GET method
const docpart = new CfnDocumentationPart(this, 'doc-part1', {
location: {
type: 'METHOD',
method: 'GET',
path: books.path
},
properties: JSON.stringify({
"status": "successful",
"code": 200,
"message": "Get method was succcessful"
}),
restApiId: api.restApiId
});
const doc = new CfnDocumentationVersion(this, 'docVersion1', {
documentationVersion: 'version1',
restApiId: api.restApiId,
description: 'this is a test of documentation'
});
// not sure if this is necessary but it made sense to me
doc.addDependsOn(docpart);
const deployment = api.latestDeployment ? api.latestDeployment: new Deployment(this,'newDeployment',{
api: api,
description: 'new deployment, API Gateway did not make one'
});
// create stage of api with documentation version
const stage = new Stage(this, 'books-api-stage1', {
deployment: deployment,
documentationVersion: doc.documentationVersion,
stageName: 'somethingOtherThanProd'
});
}
}
OUTPUT:
Created a feature request for this option here.
I had the same exact problem. The CfnDocumentationVersion call has to occur after you create all of your CfnDocumentationPart. Using your code as an example, it should look something like this:
class APISiteDocs extends Stack {
constructor(scope: Construct, id: string, props: APIResourceProps) {
super(scope, id, props);
new CfnDocumentationPart(this, 'siteDocs', {
restApiId: props.gateway.restApiId,
location: {
type: 'RESOURCE',
method: '*',
path: APISiteStack.apiBasePath,
statusCode: '405',
},
properties: JSON.stringify({
"status": "error",
"code": 405,
"message": "Method Not Allowed"
}),
});
new CfnDocumentationVersion(this, 'apiDocsVersion', {
restApiId: props.gateway.restApiId,
documentationVersion: config.app.name(`API-${config.gateway.api.version}`),
description: 'Spare-It API Documentation',
});
}
}
I'm using rn-fetch-blob library to upload some pictures to the server. But I'm getting different types of errors, such as no Boundary on Content-Type or errors with the FormData structure itself such as " of type NSMutableDictionary cannot be converted to NSString", I'm getting quite frustrated.
I got an image array like this:
var images = [
{name 'pic1', data: RNFetchBlob.wrap(imagePathHere), type: 'image/jpeg'},
{name 'pic2', data: RNFetchBlob.wrap(imagePathHere), type: 'image/jpeg'},
{name 'pic3', data: RNFetchBlob.wrap(imagePathHere), type: 'image/jpeg'},
];
Then I create the FormData as follows:
const formData = new FormData();
formData.append('id', userId) // <-- numeric type
formData.append('pictures', images);
I'm doing the post request from RNFetchBlob with 'Content-Type': 'multipart/form-data' and I pass the formData directly to it.
Any ideas on what's wrong ? I read somewhere that FormData only allows strings or blobs, should I create a Blob from my array? How can I do that?
Thanks in advance.
After a lot of struggle, I found a solution in some other github about the structure I had to pass for parameters.
This would be the correct one for my specific case.
var reqData = [
{ name: 'id', data: response.data.id }
];
this.state.images.forEach((image) => {
reqData.push({
data: image.imageFile,
filename: image.fileName.split('.')[0] + '.jpg',
name: 'pictures',
type: image.type
})
});
But basically this would be an example of it:
var params = [
{
name: 'id',
data: 43
},
{
name: 'image',
data: RNFetchBlob.wrap(imagePath),
filename: 'image1.jpg',
type: 'image/jpeg'
},
{
name: 'image',
data: RNFetchBlob.wrap(imagePath),
filename: 'image2.jpg',
type: 'image/jpeg'
},
];
So I'm building the chat functionality part for my app. I'm using sendbird sdk along with gifted chat for the UI.
My messages won't show up and I keep on getting the following warning.
'GiftedChat: _id is missing for message''
Now I've triple checked my sendbird is configured correctly, I'm logged in correctly and have also created the appropriate channel. These are both working.
Looking at the log of my messages they are posting as shown.
{
messageType: 'user',
messageId: 2122453749,
etc ...
}
But they are posting as messageId , Do I have to change the structure of this? If so how do I go about this as sendbird pre configures it already. Or can I change this in gifted-chat ?
Please take a look at the snippets of my code below.
getChannelMetaData(channel) {
if (channel) {
const self = this;
const messagesQuery = channel.createPreviousMessageListQuery();
messagesQuery.load(50, true, (messages, error) => {
if (error) {
console.error(error);
}
this.setState({
messages,
});
});
}
}
onSend(messages = []) {
const handle = this;
const sb = SendBird.getInstance();
const { channel } = this.props.navigation.state.params;
this.setState(previousState => {
channel.sendUserMessage(messages[0].text, (response, error) => {
if (!error) {
handle.getChannelMetaData(channel);
}
});
console.log(this.state.messages);
return { messages: GiftedChat.append(previousState.messages, messages) };
});
}
<GiftedChat
messages={this.state.messages}
renderBubble={bubble}
loadEarlier
renderLoadEarlier={loadEarlier}
isAnimated
keyboardShouldPersistTaps="never"
onSend={messages => this.onSend(messages)}
user={{
_id: userID,
}}
showUserAvatar
/>
You should use this format:
{
_id: 1,
text: 'message',
createdAt: new Date(),
user: {
_id: 2,
name: 'nickname',
avatar: 'YourimageURL',
},
},
If you don't follow the format specified by them, it throws this warning. So what we did for that ...we just customized our JSON object from the array of chat messages like below
let giftedChatMessages = chatMessages.map((chatMessage) => {
let gcm = {
_id: chatMessage.id,
text: chatMessage.get("text"),
createdAt: chatMessage.get("createdAt"),
user: {
_id: chatMessage.get("user").id,
name: chatMessage.get("user").get("name"),
avatar: chatMessage.get("user").get("avatarUrl")
}
};
return gcm;
});
messages={this.state.messages}
Here, {this.state.messages} should have the following structure
id:
text:
createdAt:
user: {
_id:
name:
avatar:
}
Option 1: You get all the corresponding field values as is from sendbird and update an intermediary array with the expected property names and then update this.state.messages
Option 2: Onsend function should insert the values for the following column headers in sendbird
id:
text:
createdAt:
user: {
_id:
name:
avatar:
}
So that, when you retrieve the message from sendbird, you get exactly the same fields as is expceted by giftedchat format.
I am using realm.write however my default.realm file is not being updated. I have also added another object to my schema and I can use this whilst developing my React Native app, but the default.realm file is not updated to include this new object.
I can confirm it is not being saved by opening default.realm in Realm Browser. Also, after turning my mac on this morning (after shutting down last night) and running my React Native app then data was not in my Realm when I tried to access it.
Example code:
#queries.js
import Realm from 'realm'
// Define your models and their properties
class Car {}
Car.schema = {
name: 'Car',
primaryKey: 'id',
properties: {
id: 'int',
make: 'string',
model: {type: 'string', optional: true}
}
}
class Person {}
Person.schema = {
name: 'Person',
properties: {
id: 'int',
name: 'string'
}
}
// Get the default Realm with support for our objects
let realm = new Realm({schema: [Car, Person], schemaVersion: 3});
class Queries {
async syncCars()
{
try {
let responsePromise = await fetch(`http://api.localhost.com:3000/v1/cars?auth_token=xxx`).catch(function(error) {
console.log('There has been a problem with your fetch operation: ' + error.message);
});
let responseJson = await responsePromise.json();
realm.write(() => {
responseJson.forEach((values) => {
realm.create('Car', values, true);
});
});
}
catch(error) {
console.log("error " + error);
}
}
}
export default new Queries
and then in my component code I have:
import Queries from './queries'
await Queries.syncCars();
As Kristian Dupont pointed out in the comments above, I was looking at the wrong file.
Following this answer I used realm.path in the Chrome debugger console and found the correct default.realm in a path that included /Library/Developer/CoreSimulator/Devices/. I'm not sure why OSX finder wasn't able to find this file. I'm unsure why a default.realm file was also created in my React Native directory but I have since deleted this file to avoid confusion and have had no further issues.