Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate the usage of asyncData to fetch #10839

Merged
merged 1 commit into from
Apr 29, 2024

Conversation

codyrancher
Copy link
Contributor

@codyrancher codyrancher commented Apr 18, 2024

Summary

Migrate the usage of asyncData to fetch. I'll remove the nuxt implementation of asyncData in a separate pr (3cdf3ec).

This will give us one less thing that we have to consider while working on Vue3. After the migration we will then remove all of the initialization code.

Occurred changes and/or fixed issues

Technical notes summary

Areas or cases that should be tested

The affected pages are, login, logout, setup, auth/config, and auth/roles.

All of these pages appear to be exercised through existing e2e tests.

Checklist

  • The PR is linked to an issue and the linked issue has a Milestone, or no issue is needed
  • The PR has a Milestone
  • The PR template has been filled out
  • The PR has been self reviewed
  • The PR has a reviewer assigned
  • The PR has automated tests or clear instructions for manual tests and the linked issue has appropriate QA labels, or tests are not needed
  • The PR has reviewed with UX and tested in light and dark mode, or there are no UX changes

@codyrancher codyrancher force-pushed the remove-async-data branch 4 times, most recently from 48eb5b8 to 68509e6 Compare April 25, 2024 00:33
@codyrancher codyrancher force-pushed the remove-async-data branch 3 times, most recently from 30de843 to 506adda Compare April 26, 2024 21:19
@codyrancher codyrancher added this to the v2.9.0 milestone Apr 26, 2024
Copy link
Contributor Author

@codyrancher codyrancher Apr 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This page should be scrutinized the most during review. I wasn't able to do a 1 to 1 translation between asyncData and fetch primarly due to the previously used mounted() hook.

While figuring out what the issue was I broke up and shrunk the definition of asyncData and decided to leave it. If you'd like me to go back to a 1 to 1 definition let me know, I don't think it should be an issue.

Comment on lines +38 to +40
data() {
const username = this.$cookies.get(USERNAME, { parseJSON: false }) || '';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switching the access pattern of $cookies to be consistent with $route. It doesn't matter here but it matters when defining fetch as the code is treated completely differently by nuxt.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have expected all the data to come from one place only, which is meant to be the state management, while persistent data is to be collected together in one single object. We do not want to tackle this now though, so what you did for me is already totally fine and is a good start.

this.username = this.firstLogin ? 'admin' : this.username;
this.$nextTick(() => {
this.focusSomething();
});
},

methods: {
async loadInitialSettings() {
Copy link
Contributor Author

@codyrancher codyrancher Apr 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pulled this out to reduce the amount I needed to understand while mentally parsing asyncData.

Comment on lines 138 to 140
this.username = this.firstLogin ? 'admin' : this.username;
this.$nextTick(() => {
this.focusSomething();
});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This had to be moved from mounted. Previously asyncData would be executed and the state set before mounted was called. This caused big headaches while debugging e2e tests.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's good you found out, thanks.
There's one aspect related to vendors and providers, which I am not sure we have covered in anyhow, but I guess it should be fine till other related variables are there as well.

Comment on lines +128 to +129
if ( hasLocal ) {
// Local is special and handled here so that it can be toggled
removeObject(providers, 'local');
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love how we modify const lists. I really think immutable should take place of referentially const practically everywhere.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our utilities are aimed at doing that, so it may be worth starting the conversation from that point since it's simpler.
For the record, I think me you and @rak-phillip are all in for that and we can even use ESLint for it, including Vue specific cases.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm all for it. Although I'm unsure if eslint will catch cases like this where we aren't explicitly reassigning the constant and mutating function parameters in removeObject..

const username = $cookies.get(USERNAME, { parseJSON: false }) || '';

return {
product: getProduct(),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This wasn't used anywhere in login

Comment on lines +164 to +174
this.$set(this, 'productName', productName);
this.$set(this, 'haveCurrent', !!current);
this.$set(this, 'username', me?.loginName || 'admin');
this.$set(this, 'isFirstLogin', isFirstLogin);
this.$set(this, 'mustChangePassword', mustChangePassword);
this.$set(this, 'current', current);
this.$set(this, 'v3User', v3User);
this.$set(this, 'serverUrl', serverUrl);
this.$set(this, 'mcmEnabled', mcmEnabled);
this.$set(this, 'telemetry', telemetry);
this.$set(this, 'principals', principals);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some places we use $set and others we just use assignment. Is one better or easier for migration?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are going to remove all the this.$set in the migration script with simple assignments, although it works differently between Vue2 and Vue3. The only concern is that my regex rule may not capture this case me?.loginName || 'admin' and will need to be modified by hand, but it's not a big deal.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh also that's a good point, no idea if we want specifically some cases to not be reactive, so a consistent assignment is preferred to avoid debugging later on.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$set should be an indicator that reactivity is desired if we are adding new properties to an already reactive object. I agree that assignment should be the default case, unless we're dealing with change detection caveats of Vue2

After the migration we will then remove all of the initialization code. This is motivated by the Vue3 migration.
showLocal: !hasOthers || (route.query[LOCAL] === _FLAGGED),
firstLogin: firstLoginSetting?.value === 'true',
singleProvider,
showLocaleSelector: !process.env.loginLocaleSelector || process.env.loginLocaleSelector === 'true'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing about this code is async so I moved it to data.

@codyrancher codyrancher marked this pull request as ready for review April 27, 2024 00:35
Copy link
Contributor

@cnotv cnotv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cannot see any issue or valuable point to discuss that you did not already mention.

A topic for the future out of scope and which I already expressed is the presence of async patterns all the time from the state, which is really not necessary and caused clearly headaches also here.

LGTM

Comment on lines +38 to +40
data() {
const username = this.$cookies.get(USERNAME, { parseJSON: false }) || '';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have expected all the data to come from one place only, which is meant to be the state management, while persistent data is to be collected together in one single object. We do not want to tackle this now though, so what you did for me is already totally fine and is a good start.

Comment on lines 138 to 140
this.username = this.firstLogin ? 'admin' : this.username;
this.$nextTick(() => {
this.focusSomething();
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's good you found out, thanks.
There's one aspect related to vendors and providers, which I am not sure we have covered in anyhow, but I guess it should be fine till other related variables are there as well.

Copy link
Member

@rak-phillip rak-phillip left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think that we'll be able to safely remove these lines about asyncData now that it's no longer used throughout Dashboard?

if (page.$options.asyncData) {
p.push(
promisify(page.$options.asyncData, this.context)
.then((newData) => {
for (const key in newData) {
Vue.set(page.$data, key, newData[key]);
}
})
);
}

if (hasAsyncData) {
const promise = promisify(Component.options.asyncData, app.context);
promise.then((asyncDataResult) => {
applyAsyncData(Component, asyncDataResult);
if (this.$loading.increase) {
this.$loading.increase(loadingIncrease);
}
});
promises.push(promise);
}

edit: There's quite a few more references to asyncData in shell/initialize/client.js, but I don't think it makes sense to list them all here 🙂

@cnotv
Copy link
Contributor

cnotv commented Apr 29, 2024

Do you think that we'll be able to safely remove these lines about asyncData now that it's no longer used throughout Dashboard?

As you mentioned we all agree there is a point for another issue and there are more cases, but since we do it I add a couple of comments.

There are more components using asyncData: https://github.com/search?type=code&auto_enroll=true&q=%22.asyncData%22+OR+%22+asyncData%22+repo%3Arancher%2Fdashboard

Also, let's not forget that something related to the routing can be handled with the related vue-router function. Albeit we may not want to fall in the same trap 😅

@codyrancher
Copy link
Contributor Author

Do you think that we'll be able to safely remove these lines about asyncData now that it's no longer used throughout Dashboard?

if (page.$options.asyncData) {
p.push(
promisify(page.$options.asyncData, this.context)
.then((newData) => {
for (const key in newData) {
Vue.set(page.$data, key, newData[key]);
}
})
);
}

if (hasAsyncData) {
const promise = promisify(Component.options.asyncData, app.context);
promise.then((asyncDataResult) => {
applyAsyncData(Component, asyncDataResult);
if (this.$loading.increase) {
this.$loading.increase(loadingIncrease);
}
});
promises.push(promise);
}

edit: There's quite a few more references to asyncData in shell/initialize/client.js, but I don't think it makes sense to list them all here 🙂

Absolutely. I took care of that in this PR 3cdf3ec.

@codyrancher codyrancher merged commit e211993 into rancher:master Apr 29, 2024
27 checks passed
Copy link
Member

@rak-phillip rak-phillip left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - I gave the changes a quick smoke test and everything functions as it did before.

Comment on lines +128 to +129
if ( hasLocal ) {
// Local is special and handled here so that it can be toggled
removeObject(providers, 'local');
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm all for it. Although I'm unsure if eslint will catch cases like this where we aren't explicitly reassigning the constant and mutating function parameters in removeObject..

Comment on lines +164 to +174
this.$set(this, 'productName', productName);
this.$set(this, 'haveCurrent', !!current);
this.$set(this, 'username', me?.loginName || 'admin');
this.$set(this, 'isFirstLogin', isFirstLogin);
this.$set(this, 'mustChangePassword', mustChangePassword);
this.$set(this, 'current', current);
this.$set(this, 'v3User', v3User);
this.$set(this, 'serverUrl', serverUrl);
this.$set(this, 'mcmEnabled', mcmEnabled);
this.$set(this, 'telemetry', telemetry);
this.$set(this, 'principals', principals);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$set should be an indicator that reactivity is desired if we are adding new properties to an already reactive object. I agree that assignment should be the default case, unless we're dealing with change detection caveats of Vue2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants