How to execute SQL query in bookshelf - bookshelf.js

Anyone know how to execute SQL query in bookshelf?
Something like:
bookshelf.query(sql).then(function(results) {
callback(null, results);
});

You can't execute raw queries in Bookshelf.js. If you want so, use Knex.js (used by Bookshelf) this way :
const myId = 42;
knex.raw('SELECT * FROM MyTable WHERE id = ?', [myId])
.then(result => {
console.log(result);
}, error => {
console.log(error);
});
Bookshelf.js is meant to be an ORM, where you declare each table inside your Javascript project and uses these models to retrieve data from your database.
Here's an example.
const Company = db.bookshelf.Model.extend({
tableName: 'companys',
hunters: function employees() {
return this.hasMany(Employee, 'company_id');
}
});
const Employee = db.bookshelf.Model.extend({
tableName: 'employees',
company: function company() {
return this.belongsTo(Company);
}
});
Company.where({ id: 42 }).fetch([ withRelated: 'employees' ])
.then(result => {
console.log(result);
}, error => {
console.log(error);
})

Related

Adding update property to mutation function breaks mocked result in MockProvider

I've got the following function that gets triggered on a form submission
const [register, { loading }] = useMutation(RegisterDocument);
const router = useRouter();
const onSubmit = async (values: FormValues) => {
const v = { ...values };
delete v.confirmPassword;
const res = await register({
variables: { options: v },
update: (cache, { data }) => {
cache.writeQuery<MeQuery>({
query: MeDocument,
data: {
__typename: 'Query',
me: data?.register.user,
},
});
},
});
if (res.data?.register.user) {
router.push('/');
}
};
I then have the following test to submit the form
test('it should submit form without error', async () => {
const firstName = faker.name.firstName();
const surname = faker.name.lastName();
const username = faker.internet.userName().replace('#', '');
const email = faker.internet.email();
const password = faker.internet.password(6, false, /^[a-zA-Z0-9_.-]*$/);
const cache = new InMemoryCache().restore({});
const variables = {
options: { email, firstName, password, surname, username },
};
const user = { email, firstName, surname, username, id: 1, activated: false, photo: null };
const mocks = [
{
request: { query: RegisterDocument, variables },
result: { data: { register: { errors: null, user } } },
},
];
const { queryByTestId, container } = renderWithTheme(
<MockedProvider mocks={mocks} cache={cache}>
<Register />
</MockedProvider>,
);
await updateRegisterInputs(container); // util function that updates input values for submission
await submitForm({ queryByTestId, testId: 'register-submit', loadingTestId: 'register-loading' }); // util function that submits form
await waitFor(() => expect(onPush).toBeCalledWith('/'));
});
When I run this test res returns the following
{ data: { register: {} } }
However, once I remove the update property inside the register mutation function, res returns the following.
{ data: { register: { errors: null, user: [Object] } } }
Any ideas why the mocked return value returns an empty object for the register property only when the update property function is added?
Even just instantiating the update property like so;
update: () => {}
still breaks the response from the mutation.
I realised that the graphql doc required the __typename property in the relevant places in my mocks
So I have to update the mock to include the typenames.
const user = { email, firstName, surname, username, id: 1, activated: false, photo: null, __typename: 'User' };
const mocks = [
{
request: { query: RegisterDocument, variables },
result: { data: { register: { errors: null, user, __typename: 'UserResponse' } } },
},
];

How to get multiple references in a firestore snapshot

I have a firestore collection containing post documents, each document contains a reference to an author (user) and a case document.
How do I get the user and the case in the same onSnapshot?
Here's what I'd like to do with await, but that doesn't seem to be an option with react-native-firebase.
export const firebasePostLooper = (snapshot) => {
let data = [];
snapshot.forEach(async (doc) => {
let newItem = {id: doc.id, ...doc.data()};
if (newItem.author) {
let authorData = await getDoc(newItem.author); // doesn't work with rnfirebase
if (authorData.exists()) {
newItem.userData = {userID: authorData.id, ...authorData.data()};
}
}
if (newItem.case) {
let caseData = await getDoc(newItem.case);
if (caseData.exists()) {
newItem.userData = {userID: caseData.id, ...caseData.data()};
}
}
data.push(newItem);
});
return data;
};
This doesn't work because getDoc() doesn't exist.
So I'm left with using .then()
export const firebasePostLooper = (snapshot) => {
let data = [];
snapshot.forEach((doc) => {
let newItem = {id: doc.id, ...doc.data()};
if (newItem.author) {
newItem.author
.get()
.then((res) => {
newItem.authorData = res.data();
if (newItem.case) {
newItem.case
.get()
.then((caseRes) => {
newItem.caseData = caseRes.data();
data.push(newItem);
})
.catch((err) => console.error(err));
}
})
.catch((err) => console.error(err));
} else {
data.push(newItem);
}
});
return data;
};
This second method doesn't seem to be working, data is empty at the return statement but data.push(newItem) contains the correct document with the 2 referenced documents.
You're returning data before it gets filled inside the promise. You should handle the returning of the data inside a .then() in order to return it after the promise has resolved and not before.
Take a look at this example where if we handle the emptyData object outside the promise chain, we just return the initial value before it has been filled.
let promise = new Promise((resolve, reject)=>{
setTimeout(resolve, 1000, 'foo');
})
let emptyData= [];
let notEmptyData = [];
promise
.then(res=>{
emptyData.push(res);
notEmptyData.push(res);
console.log("Full data: " + notEmptyData) // "Full data: foo"
});
console.log("Empty data: " + emptyData); // "Empty data: "

wait until first hook is complete before fetching data

I have this custom hook which fetches the query.me data from graphql. The console.log statement shows that this hook is running a number of times on page load, but only 1 of those console.logs() contains actual data.
import { useCustomQuery } from '../api-client';
export const useMe = () => {
const { data, isLoading, error } = useCustomQuery({
query: async (query) => {
return getFields(query.me, 'account_id', 'role', 'profile_id');
},
});
console.log(data ? data.account_id : 'empty');
return { isLoading, error, me: data };
};
I then have this other hook which is supposed to use the id's from the above hook to fetch more data from the server.
export const useActivityList = () => {
const { me, error } = useMe();
const criteria = { assignment: { uuid: { _eq: me.profile_id } } } as appointment_bool_exp;
const query = useQuery({
prepare({ prepass, query }) {
prepass(
query.appointment({ where: criteria }),
'scheduled_at',
'first_name',
'last_name',
);
},
suspense: true,
});
const activityList = query.appointment({ where: criteria });
return {
activityList,
isLoading: query.$state.isLoading,
};
};
The problem I am facing is that the second hook seems to call the first hook when me is still undefined, thus erroring out. How do I configure this, so that I only access the me when the values are populated?
I am bad with async stuff...
In the second hook do an early return if the required data is not available.
export const useActivityList = () => {
const { me, error } = useMe();
if (!me) {
return null;
// or another pattern that you may find useful is to set a flag to indicate that this query is idle e.g.
// idle = true;
}
const criteria = { assignment: { uuid: { _eq: me.profile_id } } } as appointment_bool_exp;
...

How can I update a document found in a query?

I want to update document found by a query. Actually I'm trying the following approach:
export const add_user = functions.https.onRequest((request, response) => {
corsHandler(request, response, () => {
const collectionReference = db.collection("users");
const query = collectionReference.where("name", "==", "John");
fbid_query.get()
.then(function(querySnapshot) {
if (querySnapshot.empty) {
console.log("John notfound");
} else {
console.log("John found!");
querySnapshot.forEach(function (documentSnapshot) {
add_update(query, "George");
});
}
response.json([]);
})
.catch(function(error) {
response.json([]);
});
});
});
.
const add_update = function (query, new_name) {
query.get(function(querySnapshot) {
querySnapshot.forEach(function(document) {
console.log("entered in forEach");
document.ref.update({
name: new_name
});
});
}).then(function () {
console.log("success");
})
.catch(function(error) {
console.error("error: ", error);
});
}
The only console.log that appears is success, although nothing is updated and John indeed exists in Cloud Firestore.
What am I doing wrong?
PS: I don't know if it matters but before I actually try to update, I call query.get() just to check if something exists (and after I call that approach to update).

Custom field not getting published in subscription apollo server

I'm trying to publish the newly added post, but the fields author and voteCount which are custom fields and reference another type were not being publish so that I got undefined on those fields.
My schema:
type Post {
id: ID!
title: String!
content: String
voteCount: Int!
author: User!
votes: [Vote!]!
createdAt: Date!
updatedAt: Date!
}
type Subscription {
Post(filter: PostSubscriptionFilter): PostSubscriptionPayload
}
input PostSubscriptionFilter {
mutation_in: [_ModelMutationType!]
}
type PostSubscriptionPayload {
mutation: _ModelMutationType!
node: Post
}
enum _ModelMutationType {
CREATED
UPDATED
DELETED
}
Resolver
Mutation: {
addPost: async (
root,
{ title, content },
{ ValidationError, models: { Post }, user },
) => {
if (!user) {
throw new ValidationError('unauthorized');
}
const post = new Post({
title,
content,
author: user.id,
});
await post.save();
pubsub.publish('Post', { Post: { mutation: 'CREATED', node: post } });
return post;
},
},
Subscription: {
Post: {
subscribe: () => pubsub.asyncIterator('Post'),
},
},
Post: {
// eslint-disable-next-line no-underscore-dangle
id: root => root.id || root._id,
author: async ({ author }, data, { dataLoaders: { userLoader } }) => {
const postAuthor = await userLoader.load(author);
return postAuthor;
},
voteCount: async ({ _id }, data, { models: { Vote } }) => {
const voteCount = await Vote.find({ post: _id }).count();
return voteCount || 0;
},
votes: async ({ _id }, data, { models: { Vote } }) => {
const postVotes = await Vote.find({ post: _id });
return postVotes || [];
},
},
And the subscription in React client:
componentWillMount() {
this.subscribeToNewPosts();
}
subscribeToNewPosts() {
this.props.allPostsQuery.subscribeToMore({
document: gql`
subscription {
Post(filter: { mutation_in: [CREATED] }) {
node {
id
title
content
updatedAt
voteCount
}
}
}
`,
updateQuery: (previous, { subscriptionData }) => {
// const result = Object.assign({}, previous, {
// allPosts: [subscriptionData.data.Post.node, ...previous.allPosts],
// });
// return result;
console.log(subscriptionData);
return previous;
},
});
}
The field voteCount is undefined:
While using queries or mutations, it get published normally, what should I do? Thank you.
The error you're seeing doesn't necessarily mean that voteCount is null -- rather it means you're trying to destructure an undefined value instead of an object. The path tells you this error occurred while attempting to resolve voteCount. You utilize destructuring within your resolve function in two places -- once with the root object and again with context. There should be a root object with you to work with, so I imagine the issue is with context.
When you set up context for a typical GraphQL server, you do so by utilizing middleware (like graphqlExpress) to essentially inject it into the request you're making. When you use subscriptions, everything is done over websockets, so the middleware is never hit and your context is therefore null.
To get around this, I think you'll need to inject the same context into your subscriptions -- you can see an example of how to do that here.

Resources