feat(api): add ServicePlugin.getPreferredColor() with parent chain inheritance#22
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
The Ligoj dashboard now displays each tool as a card with a preferred color (see ligoj/plugin-ui#15). The PR that implemented the cards (ligoj/plugin-ui#27) used a frontend-only fallback (PNG canvas extraction) because no API existed to let a plugin declare its color.
This PR introduces the data model evolution mentioned in the original issue ("Each card has a preferred color. Either guessed from the SVG icon of the tool, either provided (need data model evolution) by the plugin"). It is the backend foundation for the cleaner "plugin-provided color" path.
Changes
ServicePlugin#getPreferredColor()default method returningnull— opt-in, zero breaking changeNodeVo#preferredColorfield exposed in the REST payloadNodeHelper#applyPlugin(vo, entity, locator)invoked from the three locator-aware variants (toVo,toVoLight,toVoParameter)NodeHelper#resolvePreferredColor(entity, locator)walks the parent chain viaNode#getRefined()so a color declared at the service level (e.g.service:id) is inherited by its tools (e.g.service:id:ldap); leaf takes precedence over ancestorsServicePluginTest(default null check)NodeHelperTest: 6 tests covering the three variants, null cases, parent inheritance, child override priorityDesign rationale
Why on
ServicePlugin(notToolPluginorFeaturePlugin)The color is static branding metadata, semantically closer to
getName()/getVendor()onFeaturePluginthan to runtime methods likecheckStatus(...)onToolPlugin. Placing it onToolPluginwould have excluded service-only nodes (service:id,service:prov, ...) from declaring a color. Placing it onFeaturePluginwould be more orthogonal but lives in theligoj-bootstraprepository, requiring an additional coordinated release.ServicePluginis the pragmatic middle ground that covers both service and tool plugins without crossing repository boundaries.Why factor
applyPluginacross the threetoVo*variantsNodeHelperexposes three locator-aware variants consumed by different REST endpoints (findById,findAll, parameter-aware lookups). A factored helper guarantees the color is filled regardless of which endpoint the consumer hits, with no logic duplication. Variants without a locator (toVo(entity), ...) are intentionally left untouched: no locator means no plugin resolution, which is consistent with their current contract.Why walk up the parent chain manually
locator.getResource(id, ServicePlugin.class)already walks the parent chain to find a plugin, but returns the leaf's own value (not a composed inheritance). For colors specifically, that means a service-level color declared onIdentityResource(service:id) would not bubble down to its tool nodes (service:id:ldap) without an explicit walk-up. The explicit walk-up keeps the API ergonomic: a single override on the parent service covers all its tools by default, while individual tools can still override locally (leaf-first priority). This was validated end-to-end at runtime.Why a
nulldefault and how Jackson handles itA
nulldefault keeps backward compatibility intact: every existing plugin remains valid without modification. Jackson's default configuration on Ligoj omits null fields from the JSON payload (verified at runtime viaGET /rest/node/service:id), so consumers that don't yet know aboutpreferredColorsee no change in the API surface. The field appears in the payload only when a plugin in the chain declares a non-null value.Backward compatibility
nullreturn onServicePlugin#getPreferredColor()→ no existing plugin needs to change4.2.3-SNAPSHOT)Tests
mvn install: 487 tests passed, 0 failed, 0 skipped (+6 vs. master).End-to-end runtime verified locally:
service:id(Identity) returns the declared color viaGET /rest/node/service:idservice:id:ldapinherits the parent color viaGET /rest/node/service:id:ldap(walk-up)Follow-up PRs
ligoj/ligoj(host):NodeIcon.vuereadsnode.preferredColorand uses it as the card accent colorgetPreferredColor()with the brand color (e.g.#0052CCfor Jira,#FF9900for AWS)