tl;dr: can cypress variables be stored in some accesible place (like this or something similar) or do I have to get into an endless callback cycle if I want to access all of them for a single usage?
Long story:
the app I'm trying to cover with cypress tests runs on many different datasets so in order to prepare the test data before the test, I usually make few API calls and I'd like to work with their results:
Example:
The test should cover a "delete task" functionality. As test data, I want to create a task beforehand over our API. To do this, I need to make these calls:
Call ".../users/me" to get my userId (one of required params)
Call ".../users" to get a list of all users for particular dataset (first id is used to filter another, that is then used for assigneeId, another required param)
Call ".../tasks" with previous id's as required parameters
I currently have custom commands that handle those specific api calls while returning the result. I then call those commands and save their return as cypress variable. Should I want to do the third api call, I have to do something like this:
cy.getUserID().then((userId) => {
cy.wrap(userId).as('userId')
})
cy.getAllUsersForTenant().then((users) => {
cy.get('#userId').then((userId) => {
const result = users.find((escalationUserId) => escalationUserId !== userId)
cy.wrap(result.id).as('assigneeId')
})
})
cy.get('#assigneeId').then((assigneeId) => {
cy.get('#userId').then((userId) => {
// do the POST call with both assigneeId and userId available
})
})
Right now it's not really a big deal but I can imagine that I'l need more than 2 variables, will I have to add more nested callbacks or is there a way to store those variables at one accessible place?
I sort of figured it out by looking at other tickets - if I use function() instead of arrow functions, it is possible to share this context.
Related
Using Apollo cache as global store - for remote and local data, is very convenient.
However, while I've never used redux, I think that the most important thing about it is implementing flux: an event driven architecture in the front-end that separate logic and ensure separation of concerns.
I don't know how to implement that with Apollo. The doc says
When mutation modifies multiple entities, or if it creates or deletes entities, the Apollo Client cache is not automatically updated to reflect the result of the mutation. To resolve this, your call to useMutation can include an update function.
Adding an update function in one part of the application that handle all cache updates; by updating queries and/or fragments for the all other parts of the application, is exactly what we want to avoid in Flux / Event driven architecture.
To illustrate this, let me give a single simple example. Here, we have (at least 3 linked components)
1. InboxCount
Component that show the number of Inbox items in SideNav
query getInboxCount {
inbox {
id
count
}
}
2. Inbox list items
Component that displays items in Inbox page
query getInbox {
inbox {
id
items {
...ItemPreview
...ItemDetail
}
}
}
Both of those components read data from those GQL queries from auto generated hooks ie. const { data, loading } = useGetInboxItemsQuery()
3. AddItem
Component that creates a new item. Because it creates a new entity I need to manually update cache. So I am forced to write
(pseudo-code)
const [addItem, { loading }] = useCreateItemMutation({
update(cache, { data }) {
const cachedData = cache.readQuery<GetInboxItemsQuery>({
query: GetInboxItemsDocument,
})
if (cachedData?.inbox) {
// 1. Update items list GetInboxItemsQuery
const newItems = cachedData.inbox.items.concat(data.items)
cache.writeQuery({
query: GetInboxItemsDocument,
data: {
inbox: {
id: 'me',
__typename: 'Inbox',
items: newItems,
},
},
})
// 2. Update another query wrapped into another reusable method, here
setInboxCount(cache, newItems.length)
}
},
})
Here, my AddItem component must be aware of my different other queries / fragments declared in my application 😭Moreover, as it's quite verbose, complexity is increasing very fast in update method. Especially when multiple list / queries should be updated like here
Does anyone have recommendations about implementing a more independent components? Am I wrong with how I created my queries?
The unfortunate truth about update is that it trades simplicity for performance. A truly "dumb" client would only receive data from the server and render it, never manipulating it. By instructing Apollo how to modify our cache after a mutation, we're inevitably duplicating the business logic that already exists on our server. The only way to avoid this is to either:
Have the mutation return a larger section of the graph. For example, if a user creates a post, instead of returning the created post, return the complete user object, including all of the user's posts.
Refetch the affected queries.
Of course, often neither approach is particularly desirable and we opt for injecting business logic into our client apps instead.
Separating this business logic could be as simple as keeping your update functions in a separate file and importing them as needed. This way, at least you can test the update logic separately. You may also prefer a more elegant solution like utilizing a Link. apollo-link-watched-mutation is a good example of a Link that lets you separate the update logic from your components. It also solves the issue of having to keep track of query variables in order to perform those updates.
I'm using react-apollo#2.5.6
I have a component, when you click on it, it will based on "select" state and issue either an add or a remove operation.
Currently I'm doing this to have 2 mutations function injected to my component. Is that the correct way to do it? Am I able to just use one Mutation ( HOC ) instead of multiple ?
<Mutation mutation={ADD_STUFF}>
{(addStuff) => (
<Mutation mutation={REMOVE_STUFF}>
{(removeStuff) => {
And later in the wrapped component, I will do something like that
onClick={(e) => {
e.preventDefault()
const input = {
variables: {
userId: user.id,
stuffId: stuff.id,
},
}
// Based on selected state, I will call either add or remove
if (isSelected) {
removeStuff(input)
} else {
addStuff(input)
}
}}
Thanks
Everything is possible but usually costs time and money ;) ... in this case simplicity, readability, manageablility.
1st solution
Common mutation, f.e. named 'change' with changeType parameter.
Of course that requires API change - you need a new resolver.
2nd solution
Using graphql-tag you can construct any query from the string. Take an inspiration from this answer - with 'classic graphql HOC' pattern.
This solution doesn't require API change.
I think using two different Mutation components does not make sense. If I understand correctly, there can be two ways to solve your problem.
Using Apollo client client.mutate function to do manual mutation based on the state and set mutation and variables properties based on the new state. To access the client in current component, you need to pass along the client from parent component where it was created to child components where mutation is taking place.
Using single Mutation component inside render method of your component and setting mutation and variables attributes based on the state variable.
The approach that you are using is working as you said, but to me looks like you are delegating some logic to the UI that should be handled by the underlying service based on the isSelected input.
I think that you should create a single mutation for ADD_STUFF and REMOVE_STUFF, I would create the ADD_OR_REMOVE_STUFF mutation, and choose the add or remove behavior on the resolver.
Having one mutation is easier to maintain/expand/understand, if the logic requires something else besides add/remove, for example if you have to choose add/remove/update/verify/transform, would you nest 5 mutations?
In the previous case the single mutation could be named MULTI_HANDLE_STUFF, and only have that one mutation called from the UI.
I have a list of tasks in vuex. I have a button in a component that will add a task. I also have a backend server that will store the task in a DB.
Right now, I have it set up like this:
Button->Ajax->Backend Response->Update Vuex->Vue Updates Frontend
What I would like is:
Button->Update Vuex->Ajax/Vue each do their thing.
The issue I have is that I don't have an id for the item until the DB creates it. I could create a temporary id locally and send that to the server as a temporary id, but that seems messy. I wouldn't store it, I would just use it in vuex to update the object when it comes back from the server.
For example, let's say the database table tasks looks like this:
id | title
How would I know that the response from the server corresponds to the object I just pushed into Vuex? I can't do a find by title because it's not unique. I can't do a find by id == null because I might be creating more than one task before I get a response from the server.
I could do this in Vuex:
id | title | vuex_id
And I could use the response from the server to find the task with vuex_id that I set and update it with the id from the database.
Feels like there should be a cleaner way to make the the frontend react instantly.
Your first approach is the default (and for me the right one), go to the server and return (if it was successfully save) the id, add it to the task object and update the store.
Your second approach it's not appropriated (also for me), what if the save fails? you already have the task on the store and updated the UI, and then the task should be removed or implement some retry request method, to maintain the task in the UI. It will be harder to test and debug, and also harder to understand if you are note familiar with the code.
Button->Update Vuex->Ajax/Vue is possible, hence the reason we have Actions to perform an async operation.
That said, you can always chain actions , if the first one returns a promise say it triggers a db save, waits for it to save successfully gets back the id and then trigger a subsequent action, if it fails you may want to handle it with retries and or whatever is appropriate, something along the lines of,
actions: {
saveInDb ({ commit }) {
return new Promise((resolve, reject) => {
// set time out below is your call to save into your Db
setTimeout(() => {
// save in db
resolve(yourId)
}, 1000)
})
},
updateEverythingElse ({ dispatch, commit }) {
return dispatch('SaveInDb').then((myNewId) => {
commit('mutationToSaveId')
}).catch((e) => {})
}
}
I may not have captured everything you said completely, but just gave a brief idea how it can possibly be done from the little i have done.
Based on the edited question
This is the reason guids exist, and hence frontend/client sends in the Id, that said, looks like it might be an identity column for Id from your question, so there is then lesser options, of which you mentioned one with vuexid , the other would be combinations of the column values like a composite key could be as simple ${title}_${you_other_column}, other than this you are probably left with option 1 of your doing.
Food for thought
Along those lines, my guess is, you are doing a batch update of Array<Tasks>, if am not mistaken, so i don't see the reason for your Vuex's mutations to be per row (per task object) and that it needs to be the entire collection , you may need to rethink that portion of it as i dont know the context and the reasoning behind updating one row at a time.
What is the most performant way to handle the following situation? Is there a different way to handle this (other than the options I describe) that is more efficient? Is there a 'standard' way of managing it?
Say I have the following endpoints:
/posts
/users
GET /posts:
[{
id: 1,
title: 'Some post',
userId: 1,
...other post data
},
...more posts
]
GET /users/1:
[{
id: 1,
name: 'ExampleUser',
...other user data
},
...more users
]
I want to return a list of posts which show some basic information about the user who created them. In order to do this. I will need to fetch the list of posts and then for each post fetch the user. One user may be referenced in multiple posts.
The React components are organized as (pseudocode):
<PostsList posts={posts}>
<Post post={post}>
<UserInfo user={user} />
</Post
</PostsList>
Option A: dispatch(fetchPosts()) in PostsList, then dispatch(fetchUser(post.userId) in each Post (or in each UserInfo)
Pros: Pretty easy to implement; doesn't need specialized action (ie fetchPostsWithUserInfo())
Cons: Multiple components must be connected to the store; knowing when to render a component or a loading spinner can get into complex logic; the same user can be fetched multiple times (even if fetchUser(post.userId) dispatches an action recording which users are being fetched when the action is initiated, this doesn't seem to completely prevent duplicate calls).
Option B: handle in actions
const fetchPostsWithUsers = () => dispatch => {
dispatch(fetchPostsWithUsersStart());
fetchPostsFromServer()
.then(res => {
dispatch(fetchPostsSuccess());
const users = getListOfUniqueUserIds(res.data);
users.forEach(userId => dispatch(fetchUser(userId)));
})
.catch(err => dispatch(fetchPostsError()));
}
Pros: No duplicate calls, fewer connected components
Cons: Still have issues with lots of loading checks; possibly missing information if some calls fail; tightly couples posts call to user data fetching
Option C: Like Option B, but use a Promise.all to fetch the user data and then have only one success call (dispatch(fetchPostsWithUserDataSuccess())
Pros: Fewer loading checks, all-or-nothing so no partially missing data
Cons: All-or-nothing so if once fetch fails have to re-fetch all data; slow response time because need to wait for all individual calls to complete before any data is made available
I have attempted at length to find information on this topic, but I have not been able to find any information, which surprises me because I imagine it is a common case. It might be that I am just ignorant of the correct terminology to describe the problem, in which case I would very much appreciate knowing the correct terms.
Option A is good choice, as you'll be able to reuse each container component separately. You just need to debounce the fetch calls so the same ones don't run multiple times simultaneously.
I have two actions (each one does a different REST call to a service to collect some data), and I want to create a meta-Action which essentially triggers the two actions and aggregates the results.
I am just getting started with OpenWhisk, And I pretty much know how I would do this in the given language I am using to implement actions, but I am curious what the appropriate OpenWhisk way to do this might be?
If you want to aggregate the results, there is no other way currently than the one described by you:
Create a new action, fire the two actions (blocking=true) and merge the results.
The openwhisk module on npm makes that extra-simple, as you can invoke an array of actions there:
var openwhisk = require("openwhisk")
function main(params) {
var ow = openwhisk()
return ow.actions.invoke([
{name: "action1", blocking: true},
{name: "action2", blocking: true}
]).then(([result1, result2]) => { /* do something */ });
}
Invoking the actions blockingly, makes their results available in the response vs. not using blocking where you'll only get an activation id to get the results in an asynchronous fashion.