Skip to content

Fix storage UI#2902

Merged
joseivanlopez merged 28 commits intoagama-project:api-v2from
joseivanlopez:fix-storage-ui
Nov 26, 2025
Merged

Fix storage UI#2902
joseivanlopez merged 28 commits intoagama-project:api-v2from
joseivanlopez:fix-storage-ui

Conversation

@joseivanlopez
Copy link
Copy Markdown
Contributor

@joseivanlopez joseivanlopez commented Nov 20, 2025

Fix the storage UI after the migration to the new API.

Note: the failing tests will be adapted in a separate PR.

joseivanlopez and others added 8 commits November 21, 2025 11:10
In a distant past, the table to define a custom space policy could include
partitions, full disks and other devices. Now it only displays the partitions
of a given disk.
@joseivanlopez joseivanlopez marked this pull request as ready for review November 25, 2025 17:14
config.alias
),
kind: :partitioned_with_user
kind: IssueClasses::Config::OVERUSED
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

NP: overused sounds a bit strange. I kind of expect name like ALREADY_USED or IN_USE

error(
format(_("There is no boot device with alias '%s'"), device_alias),
kind: :no_such_alias
kind: IssueClasses::Config::ALIAS
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

name is also a bit inconsistent. I kind of expect something like NO_ALIAS

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

especially if you compare it with above NO_ROOT. I think issue should be either identified the part where is issue like ROOT, ALIAS, ENCRYPTION or by problem it describe like missing something, wrong entry, etc.

# @see Base
def error(message)
super(message, kind: :search)
super(message, kind: IssueClasses::Config::SEARCH)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

to be honest I am not sure on which level the issue classes acts...as here is generic search issue, but when alias is not found or root device is not found, it has dedicated entry. Overall I found it inconsistent in naming and in usage. For me if I need add new constant there, I will be lost how it should look like.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You are right. There should be no generic classes. Each class should correspond to a well defined issue. I'm working on a PR to fix that.

product?: Product;
l10n?: l10n.Config;
storage?: storage.Config;
product?: product.Config;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this looks strange. I think @imobachgs should probably check it as I kind of expect we should have soon proper type for product and software config here.

Copy link
Copy Markdown
Contributor Author

@joseivanlopez joseivanlopez Nov 26, 2025

Choose a reason for hiding this comment

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

Note that this PR does not define the new types related to sofware and products. It only places the existing types in the correct location. All those types will be revisited when adapting the sofware and product code to the new API.

Copy link
Copy Markdown
Contributor

@teclator teclator Nov 26, 2025

Choose a reason for hiding this comment

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

If it place the existing types in the correct location why product was removed from Config? I would expect some clarification somewhere because it is an storage PR but this is a product change.

import { Issue } from "~/api/issue";

function selectIssues(issues: Issue[]) {
return issues.filter((i: Issue) => i.scope === "storage" && i.class !== "proposal");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I see this quite fragile comparison. When any new non config class will be added, then there will be problem. What about having prefixes in classes like config.no_root or config.label?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Actually @ancorgs initially proposed something like that. @ancorgs , what do you think?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yes. That was my first thought. And I even proposed that to Imo and Iván.

But since using those prefixes is not a general idea used in other scopes and the comparison with "proposal" looked simpler, I decided to keep it simple.

I can bring back the original idea and add a "config" prefix.

/**
* Model types.
*
* Types that extend the apiModel by adding calculated properties and methods.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I really dislike this...There is apiModel which is called model and here it defined another model which is extended version of model. This is extra confusing. I think it should really use different names like extendedModel or modelCalculated. In rust where it is easy to use namespace like proposal::Storage and system::Storage it is easy to have types which identical names, but in different namespace. But in typescript having types with identical name is quite hard to read the code, especially if they collide ( basically you always have to check imports in given file to see what it talks about ).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The extended version of the model is going to dead. There will be only one model. Note that the changes in this PR are intended to make the code work with the new API, but all the bad designs are kept. Refactoring will come in another iteration.

Copy link
Copy Markdown
Contributor

@dgdavid dgdavid left a comment

Choose a reason for hiding this comment

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

Thanks for the extensive work on adapting the interface to the new API, @joseivanlopez!

I've reviewed the web part, but not in depth since I understand this is more of an adaptation rather than a full refactor, although some improvements have already been made 👌 Also, a big thanks to @jreidinger, who have done a more thorough review!

Overall it LGTM. I’ve added a few comments, some related to the changes here and some not. Feel free to consider them as feedback for thought if you think more changes are not actually not needed here.

Comment on lines +36 to +37
const keymaps = useSystem()?.keymaps;
const currentKeymap = useProposal()?.keymap;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm all for not opening discussions about enforcing strict conventions, specially considering that we don't have a formal style guide in place yet, However, I personally prefer to continue using destructuring where possible when it comes to extract data from hooks. It looks like the new useSystem hook might be returning_ undefined_, but I’m wondering if there's a (clean) way to avoid it and return an empty collection instead.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same applies for similar usage in other files in this PR.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I am not sure if at some point we will need to distinguish between no data vs not fetched.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Don't understand what you mean.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

If we always report data (e.g., {keymaps: []}, locales: []}), then we are not able to know if we have already fetched the data from the api or the data is not available yet. But as said, I don't know if we will need that.

...systemQuery,
select: selectSystem,
});
return data;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This could be the place where return an empty object instead of undefined.

return data || {}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

If we decide to always return a list, then I would move it to the select function.


return client.onEvent((event) => {
if (event.type === "IssuesChanged") {
queryClient.invalidateQueries({ queryKey: ["issues"] });
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not to be part of this PR, but we need to address the query keys thingy. Ideally, making it query export its id to be then reused. This would help to avoid typos in the future in a quite cheap way.

Comment on lines +48 to +50
queryClient.invalidateQueries({ queryKey: ["extendedConfig"] });
queryClient.invalidateQueries({ queryKey: ["storageModel"] });
queryClient.invalidateQueries({ queryKey: ["proposal"] });
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not directly related to this PR, but when we tackle the queryKey thingy, we should consider following the recommendation for using query key factories (see https://tkdodo.eu/blog/effective-react-query-keys#use-query-key-factories, and #2447). By doing so, we could easily invalidate queries under the "proposal/" namespace without worrying about potential leftovers in the future.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, we should use some robust way for defining queries. But I think we will not have nested queries anymore.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

IMHO we have to take time to think about it. If these queries conceptually belongs to "proposal" I do not see a reason for not following key factory recommendation and put them under "proposal". It will make things better to work with.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I don't get your point. Which queries do you think can be under proposal?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Those you're invalidating here. The fact that you're invalidating them under certain event suggests that these queries could be grouped in some way. "Proposal" was just an example, but the key takeaway is that if you're frequently invalidating a set of queries together, they likely share a common parent or key.

}

const solvedStorageModelQuery = (apiModel?: model.Config) => ({
queryKey: ["solvedStorageModel", JSON.stringify(apiModel)],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Weird queryKey, not sure if we want to follow that path.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This code is not new. Any suggestion?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not yet, as I do not have the full picture in mind at this time. Let's go ahead and change it when proceed.

Comment on lines +29 to +32
const systemQuery = {
queryKey: ["system"],
queryFn: getSystem,
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

For a future PR,

Suggested change
const systemQuery = {
queryKey: ["system"],
queryFn: getSystem,
};
const KEY = "system";
const systemQuery = {
queryKey: [KEY],
queryFn: getSystem,
};

and then export KEY.

@joseivanlopez joseivanlopez merged commit cce962e into agama-project:api-v2 Nov 26, 2025
8 of 9 checks passed
@@ -20,8 +20,18 @@
* find current contact information at www.suse.com.
*/

type Config = {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

So, the file was removed here but not added in api/config/product.ts

ancorgs added a commit that referenced this pull request Nov 28, 2025
…ge/ProposalPage (#2917)

## Problem

There is a hook called `useConfigIssues` that is controversial (see for
example
#2902 (comment)).
And, to be honest, ideally it should not be needed.

The point is that the storage section may have a slightly different
format (set of components) depending on quite specific logic. And that
hook exists to support that, but...

Why do we need a hook that is used in several parts of the application
just because the storage page can have different formats? Because sadly
the logic is spread along several components and files.

## Solution

Centralize in a single `<ProposalPageContent>` component all decision
making about which components will be rendered and about which issues
will be listed. Now it is SO MUCH easier to follow the logic and to know
what is going to be rendered.

That allowed us to kill the `useConfigIssues` hook, now it's logic is
used only once. No need to replicate the same criteria in several
different places.

**Bonus:** fixed an eslint-related comment that was leftover from some
previous pull request (not sure when the comment became obsolete, but it
raised a warning for me).

## Testing

All the following combinations were tested manually and the result was
the expected one. This should be converted into a unit test at some
point.

- Config is ok and supported by UI. Proposal worked. => ModelSection +
ProposalResultSection
- Config is ok and supported by UI. Proposal failed. =>
ProposalFailedInfo + ModelSection + ProposalResultSection
- Config is ok but not supported by UI. Proposal worked. =>
UnsupportedModelInfo + ProposalResultSection
- Config is ok but not supported by UI. Proposal failed. =>
UnknownConfigEmptyState
- Config is wrong, supported by UI and fixable. No proposal tried. =>
FixableConfigInfo + ModelSection
- Config is wrong, supported by UI but not fixable. No proposal tried.
=> InvalidConfigEmptyState
- Config is wrong and not supported by UI. No proposal tried. =>
InvalidConfigEmptyState
ancorgs added a commit that referenced this pull request Dec 11, 2025
## Problem

We use the so-called classes to distinguish the different kind of Agama
issues. As part of a recent pull request, the classification of the
storage issues was revisited. But, as pointed by [this
comment](#2902 (comment)),
it was done in a sub-optimal way.

The main problem with the list of classes after that pull request is
that they have a different levels of abstraction. Some classes were
quite generic (like "configEncryption" that was used for several
different problems regarding encryption) while others identified very
concrete situations (like "configNoRoot").

## Solution

Redefine the set of classes to be more consistent, without generic
classes.

## Testing

- Adapted unit tests
- Tested manually

## Progress

Still under development. So far, only the service was adapted.
imobachgs added a commit that referenced this pull request Jan 10, 2026
Merge the new HTTP API. Each PR has been already reviewed, so it should
be safe to merge it.

* #1829
* #2508
* #2772
* #2826
* #2848
* #2860
* #2863
* #2866
* #2867
* #2869
* #2870
* #2871
* #2872
* #2873
* #2874
* #2875
* #2876
* #2877
* #2880
* #2881
* #2882
* #2884
* #2885
* #2886
* #2891
* #2892
* #2893
* #2894
* #2895
* #2896
* #2897
* #2898
* #2899
* #2900
* #2901
* #2902
* #2903
* #2904
* #2908
* #2909
* #2910
* #2912
* #2913
* #2914
* #2915
* #2916
* #2917
* #2918
* #2920
* #2921
* #2923
* #2924
* #2926
* #2928
* #2929
* #2930
* #2933
* #2934
* #2935
* #2936
* #2938
* #2939
* #2942
* #2943
* #2944
* #2945
* #2946
* #2947
* #2948
* #2950
* #2951
* #2952
* #2954
* #2955
* #2956
* #2957
* #2958
* #2959
* #2960
* #2961
* #2963
* #2964
* #2965
* #2967
* #2968
* #2969
* #2970
* #2971
* #2972
* #2974
* #2975
* #2977
* #2978
* #2980
* #2982
* #2983
* #2984
* #2988
* #2989
* #2991
* #2992
* #2993
* #2994
* #2995
* #2996
* #2997
* #2999
@imobachgs imobachgs mentioned this pull request Mar 17, 2026
imobachgs added a commit that referenced this pull request Mar 17, 2026
Prepare to release version 19.

* #1829
* #2508
* #2772
* #2818
* #2826
* #2848
* #2860
* #2863
* #2864
* #2866
* #2867
* #2869
* #2870
* #2871
* #2872
* #2873
* #2874
* #2875
* #2876
* #2877
* #2880
* #2881
* #2882
* #2884
* #2885
* #2886
* #2891
* #2892
* #2893
* #2894
* #2895
* #2896
* #2897
* #2898
* #2899
* #2900
* #2901
* #2902
* #2903
* #2904
* #2908
* #2909
* #2910
* #2912
* #2913
* #2914
* #2915
* #2916
* #2917
* #2918
* #2920
* #2921
* #2923
* #2924
* #2926
* #2928
* #2929
* #2930
* #2933
* #2934
* #2935
* #2936
* #2937
* #2938
* #2939
* #2942
* #2943
* #2944
* #2945
* #2946
* #2947
* #2948
* #2949
* #2950
* #2951
* #2952
* #2954
* #2955
* #2956
* #2957
* #2958
* #2959
* #2960
* #2961
* #2963
* #2964
* #2965
* #2967
* #2968
* #2969
* #2970
* #2971
* #2972
* #2974
* #2975
* #2977
* #2978
* #2980
* #2981
* #2982
* #2983
* #2984
* #2988
* #2989
* #2990
* #2991
* #2992
* #2993
* #2994
* #2995
* #2996
* #2997
* #2998
* #2999
* #3000
* #3001
* #3002
* #3004
* #3005
* #3006
* #3007
* #3008
* #3009
* #3011
* #3012
* #3013
* #3014
* #3015
* #3016
* #3018
* #3019
* #3020
* #3021
* #3022
* #3023
* #3024
* #3025
* #3026
* #3027
* #3028
* #3029
* #3030
* #3031
* #3033
* #3034
* #3035
* #3036
* #3037
* #3039
* #3040
* #3041
* #3042
* #3043
* #3044
* #3045
* #3046
* #3047
* #3048
* #3049
* #3050
* #3051
* #3052
* #3053
* #3054
* #3055
* #3056
* #3057
* #3058
* #3060
* #3061
* #3062
* #3063
* #3064
* #3065
* #3066
* #3067
* #3068
* #3069
* #3070
* #3071
* #3072
* #3073
* #3074
* #3075
* #3076
* #3077
* #3078
* #3079
* #3086
* #3087
* #3088
* #3089
* #3090
* #3091
* #3092
* #3093
* #3094
* #3095
* #3096
* #3097
* #3098
* #3099
* #3100
* #3101
* #3102
* #3103
* #3104
* #3105
* #3106
* #3107
* #3108
* #3109
* #3110
* #3112
* #3113
* #3114
* #3115
* #3116
* #3117
* #3118
* #3119
* #3120
* #3122
* #3123
* #3124
* #3127
* #3128
* #3129
* #3130
* #3131
* #3133
* #3134
* #3135
* #3136
* #3137
* #3138
* #3139
* #3140
* #3141
* #3142
* #3143
* #3144
* #3145
* #3146
* #3147
* #3148
* #3149
* #3150
* #3151
* #3152
* #3153
* #3154
* #3155
* #3157
* #3158
* #3159
* #3160
* #3161
* #3162
* #3163
* #3164
* #3165
* #3166
* #3167
* #3168
* #3169
* #3170
* #3174
* #3175
* #3176
* #3177
* #3178
* #3179
* #3181
* #3182
* #3184
* #3185
* #3186
* #3188
* #3189
* #3190
* #3191
* #3192
* #3194
* #3195
* #3196
* #3197
* #3198
* #3199
* #3200
* #3201
* #3202
* #3203
* #3205
* #3206
* #3208
* #3209
* #3210
* #3213
* #3214
* #3215
* #3216
* #3217
* #3218
* #3219
* #3220
* #3222
* #3223
* #3224
* #3225
* #3226
* #3227
* #3228
* #3229
* #3230
* #3231
* #3232
* #3233
* #3234
* #3235
* #3236
* #3237
* #3238
* #3239
* #3240
* #3241
* #3242
* #3243
* #3244
* #3246
* #3247
* #3248
* #3250
* #3251
* #3252
* #3253
* #3254
* #3255
* #3256
* #3257
* #3258
* #3259
* #3260
* #3261
* #3262
* #3263
* #3265
* #3266
* #3267
* #3268
* #3269
* #3270
* #3271
* #3272
* #3273
* #3274
* #3275
* #3276
* #3277
* #3278
* #3279
* #3280
* #3281
* #3282
* #3283
* #3284
* #3285
* #3286
* #3287
* #3288
* #3289
* #3290
* #3291
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.

5 participants