Gatsby API endpoint - reactjs

I'm new to gatsby/graphql and I'm using the gatsby-source-custom-api plugin and based on the gatsby-config example:
// Lets assume this is the data from your API:
const exampleDataFromApi = [
{
url: "post-1",
images: [
{
url: "image-1.jpg",
modified: 1556752476267
},
{
url: "image-2.jpg",
modified: 1556752702168
}
],
author: {
firstname: "John",
lastname: "Doe"
}
}
];
// This is the content of your gatsby-config.js
// and what you need to provide as schema:
module.exports = {
plugins: [
{
resolve: "gatsby-source-custom-api",
options: {
url: {
development: "http://my-local-api.dev", // on "gatsby develop"
production: "https://my-remote-api.com" // on "gatsby build"
},
imageKeys: ["images"],
rootKey: "posts",
schemas: {
posts: `
url: String
images: [images]
author: author
`,
images: `
url: String
modified: Int
`,
author: `
firstname: String
lastname: String
`
}
}
}
]
};
I was wondering if there's a way to change the endpoint of the API based on the current page? For example current URL is https://mywebsite.com/data1 and API endpoint https://my-remote-api.com/data1.
I'm trying to reproduce this:
componentDidMount() {
fetch('https://my-remote-api.com/' + this.props.endpoint)
.then(response => response.json())
.then(data => this.setState({ data }));
}
but with graphql.

Related

next-auth custom auth window not defined

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;
}

Jest test throws "Cannot read property 'prototype' of undefined" when tested action returns a call to fetch

I'm trying to debug a Jest test in a React/Redux app; I'm running the following test (I haven't implemented faker yet).
it("Should create a SUBMIT_CONTACT_ME_FORM_SUCCESS action when contact me form submission completes successfully.", () => {
const contactMe = {
id: 1,
name: "John Shepard",
email: "shepard#n7.gov",
comments: "I am not the very model of a scientist salarian."
};
fetchMock.postOnce(contactMeURL, {
headers: { "content-type": "application/json" },
body: contactMe
});
const expectedActions = [
{
type: navbarActions.SUBMIT_CONTACT_ME_FORM_REQUEST,
contactMeForm
},
{
type: navbarActions.SUBMIT_CONTACT_ME_FORM_SUCCESS,
contactMe,
receivedAt: 1
}
;
const store = mockStore({ contactMe: null });
return store.dispatch(navbarActions.submitContactMeForm(contactMeForm, 1))
.then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});
However, my logs point to the beginning of the fetch in the following statement within the tested action.
return fetch(`${apiRoot}/contact`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(contactMeForm)
})
.then(response => response.json())
.then(json => {
if (json.err) {
dispatch(submitContactMeFormFailure(json.err, forcedTime))
} else {
dispatch(submitContactMeFormSuccess(json, forcedTime))
}
});
It seems to be a timing/lifecycle issue, but I can't isolate or correct it. I'm using the following dev dependencies.
"devDependencies": {
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.6",
"fetch-mock": "^9.11.0",
"jasmine": "^3.9.0",
"jasmine-enzyme": "^7.1.2",
"node-fetch": "^3.0.0",
"redux-mock-store": "^1.5.4"
}
This might not be the answer but try running your test with async
it("Should ... successfully.", async() => {
const contactMe = {
id: 1,
name: "John Shepard",
email: "shepard#n7.gov",
comments: "I am not the very model of a scientist salarian."
};
fetchMock.postOnce(contactMeURL, {
headers: { "content-type": "application/json" },
body: contactMe
});
const expectedActions = [
{
type: navbarActions.SUBMIT_CONTACT_ME_FORM_REQUEST,
contactMeForm
},
{
type: navbarActions.SUBMIT_CONTACT_ME_FORM_SUCCESS,
contactMe,
receivedAt: 1
};
const store = mockStore({ contactMe: null });
await store.dispatch(navbarActions.submitContactMeForm(contactMeForm, 1));
expect(store.getActions()).toEqual(expectedActions);
});

Apollo Client - How to test a component that uses multiple queries, using HOC components that use compose

I am reading over the docs for testing React/Apollo components Link. If the component has one query, it seems pretty simple to test it.
const mocks = [
{
request: {
query: GET_DOG_QUERY,
variables: {
name: 'Buck',
},
},
result: {
data: {
dog: { id: '1', name: 'Buck', breed: 'bulldog' },
},
},
},
];
it('renders without error', () => {
renderer.create(
<MockedProvider mocks={mocks} addTypename={false}>
<Dog name="Buck" />
</MockedProvider>,
);
});
My component is a little different than the one provided in the documentation.
It doesn't use the useQuery hook, instead I am opting for the HOC approach as outlined here.
I have two queries that my function uses, and so I use two graphql functions and combine them together using compose, as recommended in the docs.
My component is exported like this:
export default compose(withQueryA, withQueryB)(MyComponent);
const withQueryA = graphql(QUERY_A, {
name: "QueryA",
options: (props) => ({
variables: {
foo: props.foo,
},
}),
});
const withQueryB = graphql(QUERY_B, {
name: "QueryB ",
options: (props) => ({
variables: {
bar: props.bar,
},
}),
});
What I'm trying to do is provide the mocks object with multiple objects, each containing a request/result for the corresponding query. I just wanted to know if anyone has been testing their components in a similar way or if there is a better suggestion.
const mocks = [
{
request: {
query: QUERY_A,
variables: {
foo: "bar",
},
},
result: {
data: {
...,
},
},
},
{
request: {
query: QUERY_B,
variables: {
foo: "bar",
},
},
result: {
data: {
...,
},
},
},
];
I'm also confused about what to put in the result object. When I console.log what is actually returned to the component when making a query in production, it has the data plus error, fetchMore, loading, networkStatus. Do I have to put all those things in the mocks as well?
My feeling was correct. The result object should look something like this:
const mocks = [
{
request: {
query: QUERY_A,
variables: {
foo: "bar",
},
},
result: {
data: {
...,
},
},
},
{
request: {
query: QUERY_B,
variables: {
foo: "bar",
},
},
result: {
data: {
...,
},
},
},
];

Cannot log data from JSON-server API with GraphQL

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.

Mongoose Update array in a document does not work as expected

I'm scratching my head since a couple day on how to update the content of an array with Mongoose.
Here is my schema to begin with:
const playedGameSchema = new Schema ({
created: Date,
updated: Date,
game: {
type: Schema.Types.ObjectId,
ref: 'game'
},
creator: {
id: {
type: Schema.Types.ObjectId,
ref: 'user'
},
score: Number
},
partners: [{
id: {
type: Schema.Types.ObjectId,
ref: 'user'
},
score: Number
}]
});
module.exports = mongoose.model('PlayedGame', playedGameSchema);
Basically, what I want to achieve is to, at the same time:
- Update the creator.score (successful with dot notation).
- Update the score key for each partner (unsuccessful).
Here is the result of a document created:
{
"creator": {
"id": "5b8544fa11235d9f02a9b4f1",
"score": 0
},
"_id": "5bb6375f5f68cc5c52bc93ae",
"game": "5b45080bb1806be939bfde03",
"partners": [
{
"_id": "5bb637605f68cc5cafbc93b0",
"id": "5b85497111235d677ba9b4f2",
"score": 0
},
{
"_id": "5bb637605f68ccc70ebc93af",
"id": "5b85497111235d677ba9b4f2",
"score": 0
}
],
"created": "2018-10-04T15:53:03.386Z",
"updated": "2018-10-04T15:53:03.386Z",
"__v": 0
}
As I said, I was able to change the score of the score creator by passing something like { "creator.score": 500 } as a second parameter, then I switch to trying to update the array.
Here is my lambda function to update the score for each partner:
export const update: Handler = (event: APIGatewayEvent, context: Context, cb: Callback) => {
context.callbackWaitsForEmptyEventLoop = false;
const body = JSON.parse(event.body);
let partnersScore: object = {};
if(body.update.partners) {
body.update.partners.forEach((score, index) => {
const key = `partners.${index}.$.score`;
partnersScore = Object.assign(partnersScore, { [key]: score});
console.log(partnersScore);
});
}
connectToDatabase().then(() => {
console.log('connected', partnersScore)
PlayedGame.findByIdAndUpdate(body.id, { $set: { partners: partnersScore } },{ new: true})
.then(game => cb(null, {
statusCode: 200,
headers: defaultResponseHeader,
body: JSON.stringify(game)
}))
.catch(err => {
cb(null, {
statusCode: err.statusCode || 500,
headers: { 'Content-Type': 'text/plain' },
body: err
})});
});
}
Which passes a nice { 'partners.0.$.score': 500, 'partners.1.$.score': 1000 } to the $set.
Unfortunately, the result to my request is a partners array that contains only one empty object.
{
"creator": {
"id": "5b8544fa11235d9f02a9b4f1",
"score": 0
},
"_id": "5bb6375f5f68cc5c52bc93ae",
"game": "5b45080bb1806be939bfde03",
"partners": [
{
"_id": "5bb63775f6d99b7b76443741"
}
],
"created": "2018-10-04T15:53:03.386Z",
"updated": "2018-10-04T15:53:03.386Z",
"__v": 0
}
Can anyone guide me into updating the creator score and all partners score at the same time?
My thoughs about findOneAndUpdate method on a model is that it's better because it doesn't require the data to be changed outside of the BDD, but wanting to update array keys and another key seems very difficult.
Instead, I relied on a set/save logic, like this:
PlayedGame.findById(body.id)
.then(game => {
game.set('creator.score', update.creatorScore);
update.partners.forEach((score, index) => game.set(`partners.${index}.score`, score));
game.save()
.then(result => {
cb(null, {
statusCode: 200,
headers: defaultResponseHeader,
body: JSON.stringify(result)
})
})
.catch(err => {
cb(null, {
statusCode: err.statusCode || 500,
headers: { 'Content-Type': 'text/plain' },
body: JSON.stringify({ 'Update failed: ': err })
})});
})

Resources