How can we optimize the adding of multiple parameters to make the feature file generic to run sql query in latest cypress version? - cypress

I am using the below feature file to perform the sql query run using cy.task() in latest Cypress version. This is working fine, but is there any other clear way to perform sql query to include more parameters and to make a generic feature file line/ step definition ? Can someone please advise ?
Cypress: 10.3.0
Cypress-Cucumber-Preprocsessor
//Feature file line:
Scenario: Run the sql query to verify the user details in the User table
Given I run the sql query to get "FirstName, LastName, Email, Address" for the user email "sam#test.com"
// databaseQuery.cy.js
Then('I run the sql query to get {string} for the user email {string}', (query, userEmail) => {
cy.task('queryDb', `SELECT ${query} FROM Users WHERE Email="${userEmail}"`).then((result) => {
expect(result[0].FirstName).to.equal("Sam");
expect(result[0].LastName).to.equal("Thinker");
expect(result[0].Email).to.equal(userEmail);
expect(result[0].Address).to.equal("455 Sydney Street");
});
});

One way I can think of is to have an object of properties and values you would want to validate the response against and then use the properties from this same object in your query.
Let's assume you have an object as below.
const user = {
FirstName: "Sam",
LastName: "Thinker",
Email: "sam#test.com",
Address: "455 Sydney Street"
}
Then your code can be altered as something like below:
Scenario: Run the sql query to verify the user details in the User table
Given I run the sql query to get "userdetails" for the user email "sam#test.com"
Then(
'I run the sql query to get {string} for the user email {string}',
(query, userEmail) => {
const queryParams =
query == 'userdetails' ? Object.keys(user).join(', ') : '*';
cy.task(
'queryDb',
`SELECT ${queryParams} FROM Users WHERE Email="${userEmail}"`
).then((result) => {
expect(result[0]).to.deep.equal(user);
});
}
);
This would also mean the query params are hidden from the cucumber feature file.

Related

How to force a filter on server side to a graphql query?

Imagine the condition that I have a query called "users" that returns all the users and these users can be associated with one or more companies, so I have a type UserCompanies (I need it because it saves some more information beyond the relation). I'm using Prisma and I need to force a filter that returns only users that are of the same company as the requester.
I get the information of the company from JWT and need to inject this to the query before sending it to Prisma.
So, query should be like that:
query allUsers {
users {
name
id
status
email
userCompanies{
id
role
}
}
}
and on server side, I should transform it to: (user where is ok, just changing args)
query allUsers {
users(where: {
userCompanies_some: {
companyId: "companyId-from-jwt"
}
}) {
name
id
status
email
userCompanies(where: {
companyId: "companyId-from-jwt"
}){
id
role
}
}
}
I'm seeing a few resolutions to this, but I don't know if it is the best way:
1 - Using addFragmentToInfo, does the job to put conditions on the query, but if the query has a usercompanies already set, it gives me a conflict. Otherwise, it works fine.
2 - I can use an alias for the query, but after DB result I will need to edit all the results in array to overwrite the result.
3 - don't use info on Prisma and filter in js.
4 - Edit info(4th parameter) of type GraphqlResolveInfo

GraphQL - Cannot update a table row

Say I have a table Person with attributes id and name. The GraphQL server is all setup by Postgraphile and working as I can query and create new entry. However, I could not update it. Scratching my head over and over again and I am still unable to find out the cause for this.
This is the mutation I tried that failed me every now and then.
mutation($id: Int!, $patch: PersonPatch!) {
updatePersonById(input: { id: $id, patch: $patch }) {
clientMutationId
}
}
The variables
{
id: 1,
patch: {"name": "newname"}
}
I was using Altair GraphQL client to submit the mutation request and the error message returned was "No values were updated in collection 'people' because no values were found."
The person of id = 1 does exist, confirmed by sending a query personById over to get his name. But I just couldn't update his name.
Edit #1
Below is the gql generated by Altair GraphQL Client
updatePersonById(
input: UpdatePersonByIdInput!
): UpdatePersonPayload
input UpdatePersonByIdInput {
clientMutationId: String
patch: PersonPatch!
id: Int!
}
input PersonPatch {
id: Int
name: String
}
Assuming you're using row-level security (RLS) it sounds like the row to be updated does not pass the required security policies for the currently authenticated user.
Here's a small example; you'll want to adjust it to fit your own permissions system
create table person (id serial primary key, name text);
alter table person enable row level security;
grant select, insert(name), update(name), delete on person to graphql;
create policy select_all on person for select using (true);
create policy insert_all on person for insert with check(true);
create policy update_self on person for update using (id = current_person_id());
create policy delete_self on person for delete using (id = current_person_id());
where
create function current_person_id() returns int as $$
select nullif(current_setting('jwt.claims.person_id', true), '')::int;
$$ language sql stable;
If you need more guidance, feel free to drop into the Graphile chat.

How to refresh variables in react-apollo

Here is the scenario :
USER 1
1) Goes to login page
2) Writes email and password which are sent to the server by a mutation
3) Authentication OK -> the server returns a token and user informations (id, firstName, lastName)
4)The token and each user information are stored in a separate key in local storage
5) The user is redirected to the HomePage
6) The user goes to Profile Page
7) A query is sent to the server to retrieve all the informations about that user (thanks to the user id stored in local storage)
Here is the query :
const PROFILE_QUERY = gql`
query profileQuery($id: ID!) {
getUser(id: $id) {
firstName
lastName
email
}
}
`;
export default graphql(PROFILE_QUERY, {
options: {
variables: {
id: localStorage.getItem("id")
},
errorPolicy: "all"
}
})(ProfilePage);
8) The server returns the informations and the user can see them in the Profile Page
9) The user decides to logout
So everything is working for the first user, now a second user arrives at the same computer and the same browser.
USER 2
Same steps than the previous user from 1 to 7
The query sent to the server to retrieve user informations is not working because the id sent by the query is the id of the previous user (and a user is not allowed to retrieve informations about an other user)
If I do a browser refresh the query is sent with the good user id...
So why the id variable is not refreshed (seems like the id value in local storage is not read) at the first attempt ? How can I resolve this ?
Thank you for your help.
That happens because your options field is static and is evaluated when the file containing the query is first loaded. (I'm assuming somewhere, perhaps steps 3 or 4, the id in local storage is updated correctly for the second user.)
config.options can be an object like the one you are passing now or a function that is evaluated when needed by the query.
So to load the id from the localStorage each time instead of just once, you can do something like this:
options: () => ({
variables: {
id: localStorage.getItem("id")
},
errorPolicy: "all"
})
Then first user logged out, you need to reset Apollo store and clear local storage.

How to update all records in a collection using graphql

I'm using Graph.cool graphql as a service and am wondering how to do a mass update to the collection, similar to a SQL update.
In my case I need to update the suffix of a url, in the imageUrl column of my database. I need to swap out a {someid}_sm.jpg to {someid}_lg.jpg
How do I do that with a graphql mutation? I don't want to reload the entire dataset again and am looking for a way to do it that doesn't involve manually interating through the entire list with a graphql client.
mutation {
updatePost() // what goes here?
}
Migration script
The best approach is indeed to use a migration script that combines multiple mutations so only one HTTP request is sent to the GraphQL backend.
Consider this schema:
type Image {
id: ID!
name: String!
}
We can include the same mutation multiple times in one request with GraphQL aliases:
mutation {
first: updateImage(id: "first-id", name: "01_lg.jpg") {
id
name
}
second: updateImage(id: "second-id", name: "02_lg.jpg") {
id
name
}
}
We'll make use of this mechanism in our migration script. I'll describe it with Lokka and Node, however you can choose whatever language and GraphQL client you prefer.
First, we query all existing images to obtain their id and name:
const queryImages = async() => {
const result = await client.query(`{
images: allImages {
id
name
}
}`)
return result.images
}
Then we replace the names accordingly and construct one big request including the necessary updateImage mutations with a different GraphQL alias for each.
If your image names might contain the string sm in the {someid} part mentioned in your question, this script will break! In that case, please adjust accordingly.
const migrateImages = async(images) => {
// beware! if your ids contain the string 'sm', adjust the string replacement accordingly!
const updateMutations = _.chain(images)
.map(image => ({ id: image.id, name: image.name.replace('sm', 'lg')}))
.map(image => `
${image.id}: updateImage(id: "${image.id}", name: "${image.name}") {
id
name
}`)
.value()
.join('\n')
const result = await client.mutate(`{
${updateMutations}
}`)
console.log(`Updated ${Object.keys(result).length} images`)
console.log(result)
}
That's it. If you have to update thousands of images, batching the mutations in say groups of a hundred might be better than to batch all of them in one request. Note that mutations run sequentially on the GraphQL server.
Running the migration
Currently, I suggest the following workflow for running the migration:
Clone your project
Run the migration script on your cloned project
Verify that the migration ran successfully. Double check :)
Run the migration on your original project
You can find the code and further instructions here.
While this approach is great for migrations that are as straightforward as in your example, it's not perfect for all situations. We're already thinking about creating an integrated experience for this use case, such as an interactive migration right in your Graphcool project, with simulated migrations, checks and more. If you have suggestions, let me know in Slack.

How to search string values in GraphQL

How do you query using GraphQL in a manor similar to SQL's like operator?
Example: What users have a first name starting with jason?
select * from users where first_name like "jason%"
The short answer is: you don't.
The longer answer is that you have to write that code yourself. GraphQL isn't a database query language like SQL, it's an application query language. What that means is that GraphQL won't let you write arbitrary queries out of the box. It will only support the types of queries defined in your GraphQL schema.
If you want to be able to write a query that contains like, you have to
a) declare that in the schema, and
b) write a resolve function that fetches the data
For example, you could have this schema:
type Query {
users(firstName: String!): [User]
}
type User {
firstName: String
lastName: String
}
You would have to define the following resolve function for the users field:
{
Query: {
users(root, args){
return sql.raw('SELECT * FROM `users` WHERE `firstName` LIKE ?', args.firstName);
}
}
}
And finally write this query to get a list of firstName and lastName of all the users that match your search criteria:
{
users(firstName: 'jason%'){
firstName
lastName
}
}
Here's a post I wrote a while ago that clarifies some of the concepts around GraphQL: https://medium.com/apollo-stack/how-do-i-graphql-2fcabfc94a01
And here's a post that explains the interplay between GraphQL schemas and resolve functions: https://medium.com/apollo-stack/graphql-explained-5844742f195e
Not sure if this is relevant to you because you want it to start with "jason" (ie would return "jason bourne" but not "bourne jason") but I recently came across a way to query GraphQL in a "%Like%" manner. For your use case it would look something like this:
export const allUsersQuery = `
query allUsers($UserName: String!){
allUsers(
filter: {first_name_contains: $UserName}
) {
id
first_name
...INSERT_OTHER_FIELDS_HERE...
}
}
`;
FWIW: I did this using a GraphCool BAAS. I don't think you were using GraphCool because GraphCool doesn't allow "_" in variable names.
Hope this helps someone down the line :)

Resources