Repository navigation
Conversation
- Introduced new API commands for creating, updating, and deleting service offering categories. - Added support for associating service offerings with categories. - Updated database schema to include service offering categories. - Enhanced existing service offering commands to handle category IDs.
…e' into service_offering_category_feature
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #12144 +/- ##
============================================
+ Coverage 20.78% 20.79% +0.01%
- Complexity 21931 21953 +22
============================================
Files 6496 6503 +7
Lines 591657 592035 +378
Branches 72592 72631 +39
============================================
+ Hits 122966 123107 +141
- Misses 455346 455572 +226
- Partials 13345 13356 +11
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
This pull request has merge conflicts. Dear author, please fix the conflicts and sync your branch with the base branch. |
|
Dear @Hanarion, This is a nice feature to have. Are you considering extending it to allow a domain or account to be limited to one (or a specific list) of categories? For example, we distinguish between Core (higher-performance hardware) and Essentials (lower-performance hardware), in a tiered fashion (with different costs, of course). With "Offering Category", we could define which groups of offerings a client (domain/account) is allowed to use. What do you think? |
|
@daviftorres it could be interesting yes. |
Sounds great! |
|
@blueorangutan package |
|
@rajujith a [SL] Jenkins job has been kicked to build packages. It will be bundled with KVM, XenServer and VMware SystemVM templates. I'll keep you posted as I make progress. |
|
Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 15873 |
|
@rajujith thanks for the review. I'll check today in order to fix this issue |
Screen.Recording.2025-12-03.at.10.03.17.AM.mov |
…on/cloudstack into service_offering_category_feature
…breaking functionnality
There was a problem hiding this comment.
🟡 Changes recommended
Several introduced API/version metadata and UI/API-availability handling issues should be corrected to avoid inaccurate API docs and runtime errors for users lacking the new API permission.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (6)
Previously missed (1) — in code that hasn't changed since the last review.
engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql:30
service_offering_category.uuidis nullable, but the object implementsIdentityand the table enforces a UNIQUE constraint. In MySQL, UNIQUE allows multiple NULLs, so this doesn’t actually guarantee uniqueness unless the column is NOT NULL. Consider makinguuidNOT NULL to enforce invariants at the DB layer.
api/src/main/java/org/apache/cloudstack/api/response/ServiceOfferingResponse.java:295
- The new category fields are marked with
since = "24.0", which doesn’t match the CloudStack version format used elsewhere in this codebase (e.g.4.22.0,4.23.0). This affects API documentation/versioning metadata; please update to the correct release version for this feature (likely4.23.0).
@SerializedName("categoryid")
@Param(description = "the ID of the service offering category", since = "24.0")
private String categoryId;
@SerializedName("category")
@Param(description = "the name of the service offering category", since = "24.0")
private String categoryName;
api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCategoryCmd.java:35
since = "24.0"is inconsistent with the CloudStack API version strings used throughout the project (e.g.4.23.0). Update this to the correct CloudStack release version for the new command so API docs remain accurate.
@APICommand(name = "createServiceOfferingCategory",
description = "Creates a service offering category.",
responseObject = ServiceOfferingCategoryResponse.class,
since = "24.0",
requestHasSensitiveInfo = false,
responseHasSensitiveInfo = false)
api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java:298
- The new
categoryidparameter is annotated withsince = "24.0", which doesn’t match the CloudStack version format used elsewhere. Please update to the correct release version (likely4.23.0) so the API metadata stays consistent.
@Parameter(name = ApiConstants.SERVICE_OFFERING_CATEGORY_ID,
type = CommandType.UUID,
entityType = ServiceOfferingCategoryResponse.class,
required = false,
description = "the ID of the service offering category to associate with this offering",
since = "24.0")
private Long categoryId;
api/src/main/java/org/apache/cloudstack/api/command/admin/offering/UpdateServiceOfferingCmd.java:118
- The new
categoryidparameter is annotated withsince = "24.0", which doesn’t match the CloudStack version format used elsewhere. Please update to the correct release version (likely4.23.0) so the API metadata stays consistent.
@Parameter(name = ApiConstants.SERVICE_OFFERING_CATEGORY_ID,
type = CommandType.UUID,
entityType = ServiceOfferingCategoryResponse.class,
required = false,
description = "the ID of the service offering category to associate",
since = "24.0")
private Long categoryId;
ui/src/views/compute/wizard/ComputeOfferingSelection.vue:36
- The added
.radio-option/.ellipsisstyles aren’t applied because the slot content only renders{{ item.name }}. As-is, these new styles are dead code and long names won’t ellipsize. Either remove the unused CSS or wrap the slot content to use the classes.
- Files reviewed: 44/44 changed files
- Comments generated: 7
- Review effort level: Lite
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Pull request overview
Copilot reviewed 44 out of 44 changed files in this pull request and generated 4 comments.
Suppressed comments (3)
ui/src/components/offering/ComputeOfferingForm.vue:1
fetchCategories()unconditionally callslistServiceOfferingCategories. In the same PR, other UI flows explicitly guard this call when the API isn’t available (older management server / plugin set), but this form doesn’t—so the create/edit offering UI can break in environments where the new API command isn’t registered. Add an API-availability guard (like checking('listServiceOfferingCategories' in this.$store.getters.apis)) and handle failures by keepingcategories = [](and optionally hiding the selector).
ui/src/components/offering/ComputeOfferingForm.vue:1fetchCategories()unconditionally callslistServiceOfferingCategories. In the same PR, other UI flows explicitly guard this call when the API isn’t available (older management server / plugin set), but this form doesn’t—so the create/edit offering UI can break in environments where the new API command isn’t registered. Add an API-availability guard (like checking('listServiceOfferingCategories' in this.$store.getters.apis)) and handle failures by keepingcategories = [](and optionally hiding the selector).
server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java:1- These new mock methods return
null/falsewith TODOs. That can cause hard-to-debug NPEs if a test starts invoking them indirectly. Prefer throwingUnsupportedOperationException(fails fast) or implementing a minimal deterministic stub consistent with existing mock behavior.
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Pull request overview
Copilot reviewed 44 out of 44 changed files in this pull request and generated 3 comments.
Suppressed comments (4)
ui/src/components/offering/ComputeOfferingForm.vue:1
- The category
<a-select-option>does not set an explicit:value, which can result in the selectedform.categoryidbeingundefinedor not matching the intended category ID depending on Ant Design Vue behavior. Also,fetchCategories()unconditionally callslistServiceOfferingCategorieswithout checking API availability (unlike DeployVM/DeployVnfAppliance), which will error against older backends; add the same API-existence guard and handle rejected promises (e.g., set empty list and stop loading).
ui/src/components/offering/ComputeOfferingForm.vue:1 - The category
<a-select-option>does not set an explicit:value, which can result in the selectedform.categoryidbeingundefinedor not matching the intended category ID depending on Ant Design Vue behavior. Also,fetchCategories()unconditionally callslistServiceOfferingCategorieswithout checking API availability (unlike DeployVM/DeployVnfAppliance), which will error against older backends; add the same API-existence guard and handle rejected promises (e.g., set empty list and stop loading).
server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java:1 - These mock methods silently return
null/false, which can mask errors (or cause later NPEs) if any test hits these paths. In test mocks, prefer failing fast withUnsupportedOperationException(or implement minimal deterministic behavior) so unintended calls are immediately visible.
ui/src/views/compute/DeployVM.vue:1 - This category-fetching logic is duplicated (also in DeployVnfAppliance.vue) with identical behavior and error handling. Consider extracting a shared helper/mixin/composable (or a store action) to avoid divergence and keep UI behavior consistent when the API contract changes.
DaanHoogland
left a comment
There was a problem hiding this comment.
@Hanarion , I think you can apply some of these co-pilot comments. I didn’t go through all of them as there is one must-change, the DB upgrade file.
|
@DaanHoogland applied the stubs and made the category uuid not null. For For the FK i'd rather keep it as is, For the schema file, there is no 23 -> 24 file yet from what i see, the chain still ends at Upgrade42210to42300. Do you want me to create it here or does it come with a version bump PR? |
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Pull request overview
Copilot reviewed 44 out of 44 changed files in this pull request and generated 3 comments.
Suppressed comments (3)
ui/src/components/offering/ComputeOfferingForm.vue:1
- The form always renders the category selector for non-system offerings and always calls
listServiceOfferingCategories, but other UI code paths explicitly guard for older management servers that don’t expose this API. This can cause avoidable API errors and a broken/empty selector on older deployments. Consider conditionally showing the field and/or skippingfetchCategories()unlesslistServiceOfferingCategoriesexists in$store.getters.apis(and/or handling thecatchto keep the UI stable).
ui/src/components/offering/ComputeOfferingForm.vue:1 - The form always renders the category selector for non-system offerings and always calls
listServiceOfferingCategories, but other UI code paths explicitly guard for older management servers that don’t expose this API. This can cause avoidable API errors and a broken/empty selector on older deployments. Consider conditionally showing the field and/or skippingfetchCategories()unlesslistServiceOfferingCategoriesexists in$store.getters.apis(and/or handling thecatchto keep the UI stable).
ui/src/views/compute/wizard/ComputeOfferingSelection.vue:1 - The form-item field name
serviceofferingcategoryiddoesn’t align with the API parameter name used elsewhere (categoryid) and the prop/event naming (selectedServiceOfferingCategoryId,service-offering-category-change). Aligning the name with the API parameter (or with the component props) will reduce confusion and make it easier to trace/filter form state consistently.
|
Hi @Hanarion can you please address the review comments? |
There was a problem hiding this comment.
🟡 Changes recommended
Critical schema migration gaps and missing authorization on category management and listing APIs could break upgrades, fresh deployments, and access to the deployment wizard.
41 open issues
Add category schema to fresh installs and the correct migration · New Recreate the service offering view in the upgrade migration · New Restrict global category management to administrators · New TheFilterorder-by field is set to\"sortKey\", but the underlying DB column issort_key… This upgrade adds DDL/DML directly (CREATE/ALTER/INSERT) in a schema upgrade file that already uses… The schema upgrade adds a default row and new column/constraint in a way that is not restart-safe:…selectedServiceOfferingCategoryIdandoptions.serviceOfferingCategoriesare referenced here…selectedServiceOfferingCategoryId,options.serviceOfferingCategories, and… The FK constraint is added before the default category row is inserted. During upgrade, existing… Authorize listing categories for deployment roles · New Link category action events to the affected resource · New Render category labels in the overflow dropdown · New The hard-coded default category ID (1L) is a fragile “magic number” that tightly couples runtime… Hardcoding the default category asid=1is brittle (schema or deployments could evolve, and it’s… The FK creation uses a rawALTER TABLE ... ADD CONSTRAINT, which is not idempotent. Since the…getCategoryId()is defined as a primitivelong, but the category association is treated as…fetchServiceOfferingCategories()callslistServiceOfferingCategoriesunconditionally. In the…fetchServiceOfferingCategories()callslistServiceOfferingCategoriesunconditionally. In the… Hard-coding the default category id as1Lbakes a schema assumption into application logic. A… DeployVM passes options.serviceOfferingCategories / selectedServiceOfferingCategoryId into…
And 21 more that still need to be addressed.
4 resolved since last review
The migration uses a rawALTER TABLE ... ADD CONSTRAINTwhich is not idempotent; re-running the… Thesince = "24.0"metadata looks inconsistent with the existing versioning scheme in the… Theuuidcolumn is nullable while also being declared UNIQUE. This allows multiple rows with…SERVICE_OFFERING_CATEGORY_NAMEis introduced but never referenced anywhere in this PR (no…
Review effort: Lite
There was a problem hiding this comment.
🟡 Changes recommended
The migration has unresolved idempotency and view-recreation issues that can break retries and cause existing installations' service-offering queries to fail.
40 open issues
Recreate the service offering view in the upgrade · New Restrict global category management to administrators Recreate the service offering view in the upgrade migration Add category schema to fresh installs and the correct migration TheFilterorder-by field is set to\"sortKey\", but the underlying DB column issort_key… This upgrade adds DDL/DML directly (CREATE/ALTER/INSERT) in a schema upgrade file that already uses… The schema upgrade adds a default row and new column/constraint in a way that is not restart-safe:…selectedServiceOfferingCategoryIdandoptions.serviceOfferingCategoriesare referenced here…selectedServiceOfferingCategoryId,options.serviceOfferingCategories, and… The FK constraint is added before the default category row is inserted. During upgrade, existing… Make foreign-key migration idempotent · New Link category action events to the affected resource The hard-coded default category ID (1L) is a fragile “magic number” that tightly couples runtime… Hardcoding the default category asid=1is brittle (schema or deployments could evolve, and it’s… The FK creation uses a rawALTER TABLE ... ADD CONSTRAINT, which is not idempotent. Since the…fetchServiceOfferingCategories()callslistServiceOfferingCategoriesunconditionally. In the…fetchServiceOfferingCategories()callslistServiceOfferingCategoriesunconditionally. In the… Hard-coding the default category id as1Lbakes a schema assumption into application logic. A… DeployVM passes options.serviceOfferingCategories / selectedServiceOfferingCategoryId into… cloneServiceOffering always passes the source offering category (sourceOffering.getCategoryId())…
And 20 more that still need to be addressed.
3 resolved since last review
Review effort: Lite
|
@Hanarion , the db upgrade part looks good now. I won't have time to go through all the code for at least a couple of weeks, so you'll have to find reviews/testing resources elsewhere, but thank you for the update. |
|
This pull request has merge conflicts. Dear author, please fix the conflicts and sync your branch with the base branch. |



Description
When using Cloudstack, when creating instance with a lot of offerings, it could be hard to differentiate between all of the offerings. For that, categories could be useful.
This pull request introduces the concept of "Service Offering Categories" to the API, allowing service offerings to be grouped, managed, and queried by category. It adds a new interface for categories, updates the API to support creating, updating, deleting, and listing categories, and enables associating service offerings with a category. The changes also extend existing API commands and responses to work with categories.
Service Offering Category API Support:
CreateServiceOfferingCategoryCmd,DeleteServiceOfferingCategoryCmd,UpdateServiceOfferingCategoryCmd, andListServiceOfferingCategoriesCmd, enabling full CRUD operations and listing for categories.ConfigurationServiceinterface to include methods for creating, deleting, and updating service offering categories.ServiceOfferingCategorythat defines category properties and behaviors.API Parameter and Response Enhancements:
SERVICE_OFFERING_CATEGORY_ID) inApiConstants, and updated related commands (CreateServiceOfferingCmd,UpdateServiceOfferingCmd,ListServiceOfferingsCmd) to accept or filter by category. The category name is returned in responses under thecategorykey.ResponseGeneratorto support generating responses for service offering categories.Service Offering Model Update:
getCategoryId()method to theServiceOfferinginterface, allowing offerings to be associated with a specific category.Types of changes
Feature/Enhancement Scale or Bug Severity
Feature/Enhancement Scale
Bug Severity
Screenshots (if appropriate):
How Has This Been Tested?
On my dev environment, through the API and cloudmonkey
How did you try to break this feature and the system with this change?
Those changes should not break any features as it is only adding a new column to serviceoffering and adding a new table, it is only a way to filter and categorize.
--
I'm sorry if the formatting isn't perfect, i couldn't get the pre-commit to work, and it is my first code PR.
Warning, i put the SQL where i thought it made sense, but i think you will want to move it where it really should be.