I am using Strapi, Sqlite3 and React.
I want to send a form with a file attached.
I have a Job model, which looks like this:
{
"connection": "default",
"collectionName": "jobs",
"info": {
"name": "job",
"description": ""
},
"options": {
"increments": true,
"timestamps": true,
"comment": ""
},
"attributes": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"email": {
"type": "string"
},
"resume": {
"model": "file",
"via": "related",
"plugin": "upload"
},
"jobcategory": {
"model": "jobcategory",
"via": "jobs"
}
}
}
I am sending text input with submitCareer method, and uploadFile for uploading:
export async function submitCareer(url, formValues) {
try {
const entries = await rootUrl.createEntry(url, formValues);
return entries;
} catch (err) {
console.log(err);
}
}
export async function uploadFile(formValues) {
try {
const upload = await rootUrl.upload(formValues);
return upload;
} catch (err) {
console.log(err);
}
}
This is the usage in my Career component:
const handleSubmit = (event) => {
const formData = new FormData();
formData.append("files", fileInput.current.files[0]);
submitCareer('jobs', values);
uploadFile(formData);
setValues({
firstName: '',
lastName: '',
email: '',
resume: null
})
event.preventDefault();
}
I get this response:
{
"id": 66,
"firstName": "John",
"lastName": "Doe",
"email": "john#gmail.com",
"jobcategory": null,
"lname": null,
"created_at": 1561988031279,
"updated_at": 1561988031279,
"resume": {}
}
So, how can i connect resume with Job model?
Linking model to a file
You must create process with to two steps:
Create File -> POST /upload.
Create Jobs with id from FileResponse -> POST /jobs.
Example:
const handleSubmit = (event) => {
const formData = new FormData();
formData.append("files", fileInput.current.files[0]);
resumeUploadFile = await uploadFile(formData);
const jobsInput = {...jobs, ...{resume: resumeUploadFile.id}}
await submitCareer('jobs', jobsInput);
setValues({
firstName: '',
lastName: '',
email: '',
resume: null
})
event.preventDefault();
}
https://strapi.io/documentation/3.0.0-beta.x/guides/upload.html#file-upload
Linking files to an entry
You can linking too file to an entry wich is created, then you create first Jobs and then link upload ResumeFile with added new fields like refId from Jobs (Jobs -> id), ref in your case jobs and field=resume
https://strapi.io/documentation/3.0.0-beta.x/guides/upload.html#examples
When you want to upload a file and link it to an entry.
You have to first create the entry if it's not already done.
And then upload your file by sending the image information of the entry.
All the documentation is here https://strapi.io/documentation/3.0.0-beta.x/guides/upload.html#examples
Related
I am trying to use next-auth with my backend but it doesn't work. I use version 4 with typescript. The error is
{error: 'window is not defined', status: 200, ok: true, url: null}
Why?????. Thanks a lot.
My custom API /login result is
{
"data": {
"username": "test",
"users": {
"id": 2,
"username": "test",
"email": "test#test.com",
"createdAt": "2021-05-24",
"updatedAt": "2021-05-24",
"name": "John Smith",
"id_groups": 99,
"groups": "guest",
"avatar": null
},
"timestamp": 1646808511,
"jwt": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiG9.eyJpc3MiOiJodHRwOlwvXC90d2luYXBwLml0IiwiYXVkIjoiaHR0cDpcL1wvdHdpbmFwcC5pdCIsImlhdCI6MTM1Njk5OTUyNCwibmJmIjoxMzU3MDAwMDAwLCJleHAiOjE2NDY4MTIxMTEsImRhdGEiOiJtYXJjb2JvbmNpIn0.R1aAX99GHmoSPRKv4Vnzso8iRjUhrDWhPEdq4oql_r0"
},
"status": "",
"code": 200
}
Now, I'm try to configure next auth
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import gApi from "../../../api/gApi";
export default NextAuth({
session: {
strategy: "jwt",
},
providers: [
CredentialsProvider({
name: "credentials",
credentials: {
username: {label: "Username",type: "text", placeholder: "username"},
password: { label: "Passwort", type: "password" },
},
async authorize(credentials) {
const resp = await gApi.post("/login", JSON.stringify(credentials));
const user = resp.data;
console.log('CALL MY API');
console.log(resp);
if ( resp.status && user) {
return user;
}
return null;
},
}),
],
callbacks: {
async jwt({ token, user, account, isNewUser }) {
if (user) {
if (user.jwt) {
token = { accessToken: user.jwt };
}
}
return token;
},
async session({ session, token }) { // this token return above jwt()
session.accessToken = token.accessToken;
return session;
},
},
pages: {
signIn: "/auth/Login",
},
});
In my login page I have e simple form and i call with:
const onSubmit: SubmitHandler<FormData> = async data => {
const resp: any = await signIn("credentials", {
username: data.username,
password: data.password,
redirect: false,
});
console.log('RESPO signin');
console.log(resp);
if (resp && !resp.error) {
router.replace('/')
} else return;
}
I'm using Supabase for authentication and I get a user object when logging in.
This is how it looks like:
{
"id": "cb43b195-22cc-48c8-946a-d323f70165bd",
"aud": "authenticated",
"role": "authenticated",
"email": "joe#*******.com",
"email_confirmed_at": "2022-01-26T18:34:31.105402Z",
"phone": "",
"confirmed_at": "2022-01-26T18:34:31.105402Z",
"last_sign_in_at": "2022-02-01T18:00:27.998776Z",
"app_metadata": {
"provider": "github",
"providers": [
"github"
]
},
"user_metadata": {
"avatar_url": "https://avatars.githubusercontent.com/u/93337091?v=4",
"email": "joe#*******.com",
"email_verified": true,
"full_name": "Joe",
"iss": "https://api.github.com",
"name": "Joe",
"preferred_username": "joe",
"provider_id": "93337091",
"sub": "93337091",
"user_name": "joe"
},
"identities": [
{
"id": "93337091",
"user_id": "cb43b195-22cc-48c8-946a-d323f70165bd",
"identity_data": {
"avatar_url": "https://avatars.githubusercontent.com/u/93337091?v=4",
"email": "joe#*******.com",
"email_verified": true,
"full_name": "Joe",
"iss": "https://api.github.com",
"name": "Joe",
"preferred_username": "joe",
"provider_id": "93337091",
"sub": "93337091",
"user_name": "joe"
},
"provider": "github",
"last_sign_in_at": "2022-01-26T18:34:31.102361Z",
"created_at": "2022-01-26T18:34:31.102403Z",
"updated_at": "2022-01-26T18:34:31.102403Z"
}
],
"created_at": "2022-01-26T18:34:31.098348Z",
"updated_at": "2022-01-26T18:37:12.766+00:00",
"username": "joe",
"avatar_url": "0.181358731179603.png",
"website": null }
I'm trying to access any property but for instance, if I try to render {user.username} I get a "Cannot read username property of null" error.
Any idea why that happens?
This is the context that gives the user info - I'm using it to provide the auth data:
import { createContext, useState, useEffect, useContext } from "react";
import { supabase } from "../utils/supabase";
import { useRouter } from "next/router";
const Context = createContext();
const Provider = ({ children }) => {
const router = useRouter();
const [user, setUser] = useState(supabase.auth.user());
useEffect(() => {
const getUserProfile = async () => {
const sessionUser = supabase.auth.user();
if (sessionUser) {
const { data: profile } = await supabase
.from("profiles")
.select("*")
.eq("id", sessionUser.id)
.single();
setUser({
...sessionUser,
...profile,
});
}
};
getUserProfile();
supabase.auth.onAuthStateChange(() => {
getUserProfile();
});
}, []);
const login = async () => {
await supabase.auth.signIn({
provider: "github",
});
};
const logout = async () => {
await supabase.auth.signOut();
setUser(null);
router.push("/");
};
const exposed = {
user,
login,
logout,
};
return <Context.Provider value={exposed}>{children}</Context.Provider>;
};
export const useUser = () => useContext(Context);
export default Provider;
Thanks!
Could be because you are trying to access the user information before the API returns the data. In the component you are rendering the user data, check if the user exists first before returning your component.
if(!user) {
return <p>Loading...</p>
}
I had a similar issue and changing {user.username} to {user?.username} fixed the error for me. I am not exactly sure why, but hopefully this might help somebody.
I have this code in React 17
useEffect(() => {
getLocalJson('../json/login/login.json', props.headers)
.then(resp => {
setFields(resp);
});
}, [props.headers]);
And the getLocalJson method is in a different file:
export const getLocalJson = async (url, headers) => {
console.log(url)
const resp = await fetch(url, {'headers': headers});
const json = await resp.json();
return json;
}
However the call to load the local JSON file from the public folder is:
Request URL: http://localhost:3000/json/login/%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5Clogin.json
Ths is the JSON
[
{
"order": 0,
"systemName": "title",
"friendlyName": "Login",
"dataType": {
"type": "TITLE"
}
},
{
"order": 1,
"required": true,
"systemName": "username",
"friendlyName": "Username",
"errorMsg": "Invalid username",
"dataType": {
"type": "TEXT"
}
},
{
"order": 2,
"required": true,
"systemName": "password",
"friendlyName": "Password",
"errorMsg": "Invalid password",
"dataType": {
"type": "PASSWORD"
}
},
{
"order": 3,
"systemName": "title",
"friendlyName": "Login",
"dataType": {
"type": "BUTTON",
"submit": true
}
}
]
And it makes the call over and over and over
This exact code works on my ubuntu dev box, but is failing as abovw on my windows box
I think there is some issue with the way you are passing down the headers, look into the documentation to have a better idea.
Put your function in the body of your component where you're using useEffect and wrap it with useCallback like this:
const getLocalJson = useCallback( async (url, headers) => {
console.log(url)
const resp = await fetch(url, {'headers': headers});
const json = await resp.json();
return json;
},[])
I have built an API with JSON-server and I am trying to fetch the data from it using React-Apollo Client.
I'm trying to log the data from API on the console with Query tag, restructure and print the data variable using console.log().
I have no idea why the function is getting print via console.log().
I have the current setup:
JSON server is running on PORT 4000
Server is running on PORT 5000
Client is running on PORT 3000
I am already using CORS tool
Below is my component:
const BOOKS_QUERY = gql`
query BooksQuery {
books {
title
author
editionYear
}
}
`;
<Query query={BOOKS_QUERY}>
{({ loading, error, data }) => {
if (loading) return <h4>Loading...</h4>;
if (error) console.log(error);
console.log(data);
return <h1>test</h1>;
}}
</Query>
The content below is code for my schema:
const BookType = new GraphQLObjectType({
name: 'Book',
fields: () => ({
id: { type: GraphQLInt },
title: { type: GraphQLString },
author: { type: GraphQLString },
editionYear: { type: GraphQLInt }
})
});
//Root Query
const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: {
books: {
type: new GraphQLList(BookType),
resolve(parent, args) {
return axios.get('http://localhost:4000/books').then((res) => res.data);
}
},
book: {
type: BookType,
args: {
id: { type: GraphQLInt }
},
resolve(parent, args) {
return axios.get(`http://localhost:4000/books/${args.id}`).then((res) => res.data);
}
}
}
});
module.exports = new GraphQLSchema({
query: RootQuery
});
API:
{
"books": [
{
"id": "1",
"title": "Java How To Program",
"author": "Deitel & Deitel",
"editionYear": "2007"
},
{
"id": "2",
"title": "Patterns of Enterprise Application Architecture",
"author": "Martin Fowler",
"editionYear": "2002"
},
{
"id": "3",
"title": "Head First Design Patterns",
"author": "Elisabeth Freeman",
"editionYear": "2004"
},
{
"id": "4",
"title": "Internet & World Wide Web: How to Program",
"author": "Deitel & Deitel",
"editionYear": "2007"
}
]
}
I only expect the API data to be logged on console.
Later I will render that data on screen.
My document looks like this:
{
"data": {
"eventId": "20161029125458-df-d",
"name": "first",
"purpose": "test",
"location": "yokohama",
"dateArray": [],
"attendees": [
{
"attendeeId": "2016102973634-df",
"attendeeName": "lakshman",
"personalizedDateSelection": {}
},
{
"attendeeId": "2016102973634-tyyu",
"attendeeName": "diwaakar",
"personalizedDateSelection": {}
}
]
}
}
Say, I need to update the attendee JSON array with attendeeId: 2016102973634-df. I tried many ways ways using update and condition expression, but no success.
Here is my try:
const params = {
TableName: "event",
Key: {
"eventId": eventId
},
UpdateExpression: "SET attendees[???] = ",
ConditionExpression: attendees.attendeeId = "2016102973634-df",
ExpressionAttributeValues: {
":attendee" : attendeeList
},
ReturnValues: "ALL_NEW"
};
dynamo.update(params, (err, data) => {
if (err) {
return reject(err);
}
console.log(data.Attributes);
});
Could not find any resources for updating an Json in a array.
After #notionquest's comment:
- Have not used any JsonMarshaller. Initially I added the empty array to attendees field like this:
{
"eventId": "20161029125458-df-d",
"name": "first",
"purpose": "test",
"location": "yokohama",
"dateArray": [],
"attendees": []
}
and then When a new attendee comes I add it to the attendees property like this:
const attendee = {
"attendeeName": "user1",
"personalizedDateSelection": {"today": "free"}
}
const attendeeList = [attendee];
const eventId = "20161029125458-df-d";
const params = {
TableName: "event",
Key: {
"eventId": eventId
},
UpdateExpression: "SET attendees = list_append(attendees, :attendee)",
ExpressionAttributeValues: {
":attendee" : attendeeList
},
ReturnValues: "ALL_NEW"
};
dynamo.update(params, (err, data) => {
if (err) {
return reject(err);
}
console.log("in update dynamo");
console.log(data.Attributes);
});
As you have seen in the above snippets, initially I add empty [] array and add a new attendee using the above code. Now, How do I update a specific JSON in an array. If you say that is not possible, what else can I try?
Should I try this :
Get the Full JSON.
Manipulate the JSOn and change the things I want in my nodeJS.
And then update the new JSON to dynamoDB.
But this consumes two calls to dynamoDB which seems to be inefficient.
Would like to know If there is any round way ?
you can store the index of list. while updating the list we can use them. For example ,
{
"data": {
"eventId": "20161029125458-df-d",
"name": "first",
"purpose": "test",
"location": "yokohama",
"dateArray": [],
"attendees": [
{
"index":0,
"attendeeId": "2016102973634-df",
"attendeeName": "lakshman",
"personalizedDateSelection": {}
},
{
"index":1,
"attendeeId": "2016102973634-tyyu",
"attendeeName": "diwaakar",
"personalizedDateSelection": {}
}
]
}
}
const params = {
TableName: "event",
Key: {
"eventId": eventId
},
UpdateExpression: "SET attendees[attendee.index].attendeeName = :value",
ExpressionAttributeValues: {
":value" : {"S":"karthik"}
},
ReturnValues: "ALL_NEW"
};
dynamo.update(params, (err, data) => {
if (err) {
return reject(err);
}
console.log(data.Attributes);
});
An example of an update query:
Data structure (saved in DynamoDB)
{
tenant_id: 'tenant_1',
users: {
user1: {
_id: 'user1',
email_address: 'test_email_1#gmail.com'
},
user2: {
_id: 'user2',
email_address: 'test_email_2#gmail.com'
}
}
}
Data for update (used in the params)
var user = {
email_address: 'updated#gmail.com'
}
Params
var params = {
TableName: 'tenant-Master',
Key: {
"tenant_id": 'tenant_1'
},
UpdateExpression: "set #users.user1 = :value",
ExpressionAttributeNames: {
"#users": "users"
},
ExpressionAttributeValues: {
":value": user,
},
};
Explanation
By switching to a map of maps from an array of maps we can now use UpdateExpression: "set #users.user1 = :value" to update our nested object at the map of users with the id of user1.
NOTE: This method as is will REPLACE the entire map object at users.user1. Some changes will need to be made if you want to keep pre-existing data.
I could not find any answer to query and update the JSON-array. I think this may be AWS profitable motive to not allow those features. If you need to query on a particular ID other than primary key, you need to make a secondary index which is cost effective. This secondary index cost is additional to the dyn
amoDB table cost.
Since, I did not want to pay extra bucks on secondary index, I changed my dynamoDB schema to the following:
{
"data": {
"eventId": "20161029125458-df-d",
"name": "first",
"purpose": "test",
"location": "yokohama",
"dateArray": [],
"attendees": {
"2016102973634-df": {
"attendeeId": "2016102973634-df",
"attendeeName": "lakshman",
"personalizedDateSelection": {}
},
"2016102973777-df": {
"attendeeId": "2016102973777-df",
"attendeeName": "ffff",
"personalizedDateSelection": {}
}
}
}
}
Changing attendees from [] to {}. This allows me the flexibility to query particular attendeeId and change the entire JSON associated with that. Even though, this is a redundant step, I do not want to spend extra bucks on my hobby project.