@skyhook-io/k8s-ui 1.10.4 → 1.10.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -5
- package/src/components/applications/ApplicationDetail.test.tsx +46 -0
- package/src/components/applications/ApplicationDetail.tsx +14 -15
- package/src/components/topology/K8sResourceNode.tsx +5 -0
- package/src/components/topology/TopologyGraph.tsx +85 -5
- package/src/components/trace/ReachabilityGraph.tsx +503 -0
- package/src/components/trace/ReachabilityView.tsx +972 -0
- package/src/components/trace/TracePanel.test.ts +172 -0
- package/src/components/trace/TracePanel.tsx +558 -0
- package/src/components/trace/TraceSummary.test.ts +213 -0
- package/src/components/trace/TraceSummary.tsx +168 -0
- package/src/components/trace/inClusterSummary.test.ts +98 -0
- package/src/components/trace/inClusterSummary.ts +81 -0
- package/src/components/trace/index.ts +19 -0
- package/src/components/trace/podReach.test.ts +71 -0
- package/src/components/trace/podReach.ts +37 -0
- package/src/components/trace/probe-display.test.ts +107 -0
- package/src/components/trace/probe-display.ts +37 -0
- package/src/components/trace/reachFixtures.ts +261 -0
- package/src/components/trace/reachGraphModel.test.ts +1407 -0
- package/src/components/trace/reachGraphModel.ts +1521 -0
- package/src/components/trace/reachInspector.test.ts +669 -0
- package/src/components/trace/reachInspector.ts +667 -0
- package/src/components/trace/reachMarks.test.ts +667 -0
- package/src/components/trace/reachMarks.ts +579 -0
- package/src/components/trace/reachOrigins.test.ts +392 -0
- package/src/components/trace/reachOrigins.ts +437 -0
- package/src/components/trace/reachVerdict.test.ts +537 -0
- package/src/components/trace/reachVerdict.ts +601 -0
- package/src/components/trace/traceFingerprint.test.ts +214 -0
- package/src/components/trace/traceFingerprint.ts +122 -0
- package/src/components/trace/traceToSubgraph.test.ts +623 -0
- package/src/components/trace/traceToSubgraph.ts +463 -0
- package/src/components/trace/types.ts +389 -0
- package/src/components/ui/InClusterConsentDialog.test.tsx +83 -0
- package/src/components/ui/InClusterConsentDialog.tsx +141 -0
- package/src/components/ui/index.ts +1 -0
- package/src/components/workload/WorkloadView.tsx +58 -5
- package/src/components/workload/index.ts +1 -1
- package/src/components/workload/reachabilityTab.test.ts +47 -0
- package/src/index.ts +5 -0
- package/src/theme/components.css +38 -0
- package/src/theme/variables.css +5 -0
- package/src/types/core.ts +8 -0
- package/src/utils/application-history.test.ts +1 -1
- package/src/utils/applications.test.ts +63 -1
- package/src/utils/applications.ts +27 -0
- package/src/utils/inClusterConsent.test.ts +109 -0
- package/src/utils/inClusterConsent.ts +72 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/navigation.test.ts +61 -0
- package/src/utils/navigation.ts +11 -9
- package/src/utils/pluralize.test.ts +80 -1
- package/src/utils/pluralize.ts +34 -0
package/src/utils/navigation.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SelectedResource, ResourceRef, APIResource } from '../types/core'
|
|
2
|
-
import { englishPlural } from './pluralize'
|
|
2
|
+
import { englishPlural, englishSingular, isEnglishPlural } from './pluralize'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Canonical callback type for navigating to a resource.
|
|
@@ -103,6 +103,15 @@ export function kindToPlural(kind: string): string {
|
|
|
103
103
|
}
|
|
104
104
|
if (aliases[kindLower]) return aliases[kindLower]
|
|
105
105
|
|
|
106
|
+
// Keep the idempotence contract alive for CRD plurals that neither map knows
|
|
107
|
+
// yet: the discovery map arrives one round-trip after a cold direct-URL load,
|
|
108
|
+
// and English-pluralizing an already-plural slug yields `scheduleses`. An
|
|
109
|
+
// all-lowercase input is an API resource name rather than a Kind (Kubernetes
|
|
110
|
+
// Kinds are PascalCase), and isEnglishPlural separates a real plural from a
|
|
111
|
+
// singular that merely ends in 's' — `ingress` and `nodeclass` must still be
|
|
112
|
+
// pluralized, `schedules` and `validatingpolicies` must not.
|
|
113
|
+
if (kind === kindLower && isEnglishPlural(kindLower)) return kindLower
|
|
114
|
+
|
|
106
115
|
// Fallback: English pluralization rules (shared with pluralize() in
|
|
107
116
|
// utils/pluralize.ts so a rule change updates both call paths).
|
|
108
117
|
return englishPlural(kindLower)
|
|
@@ -125,14 +134,7 @@ export function pluralToKind(plural: string): string {
|
|
|
125
134
|
}
|
|
126
135
|
|
|
127
136
|
// Fallback: basic de-pluralization + capitalize first letter
|
|
128
|
-
|
|
129
|
-
if (singular.endsWith('ies')) {
|
|
130
|
-
singular = singular.slice(0, -3) + 'y'
|
|
131
|
-
} else if (singular.endsWith('ses') || singular.endsWith('xes') || singular.endsWith('ches') || singular.endsWith('shes')) {
|
|
132
|
-
singular = singular.slice(0, -2)
|
|
133
|
-
} else if (singular.endsWith('s')) {
|
|
134
|
-
singular = singular.slice(0, -1)
|
|
135
|
-
}
|
|
137
|
+
const singular = englishSingular(lower)
|
|
136
138
|
return singular.charAt(0).toUpperCase() + singular.slice(1)
|
|
137
139
|
}
|
|
138
140
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import { englishPlural, pluralNoun, pluralize } from './pluralize'
|
|
2
|
+
import { englishPlural, englishSingular, isEnglishPlural, pluralNoun, pluralize } from './pluralize'
|
|
3
3
|
|
|
4
4
|
describe('englishPlural', () => {
|
|
5
5
|
describe('default +s', () => {
|
|
@@ -96,3 +96,82 @@ describe('pluralize', () => {
|
|
|
96
96
|
expect(pluralize(2, 'box')).toBe('2 boxes')
|
|
97
97
|
})
|
|
98
98
|
})
|
|
99
|
+
|
|
100
|
+
describe('englishSingular', () => {
|
|
101
|
+
it('inverts englishPlural for regular plurals', () => {
|
|
102
|
+
expect(englishSingular('clusters')).toBe('cluster')
|
|
103
|
+
expect(englishSingular('policies')).toBe('policy')
|
|
104
|
+
expect(englishSingular('classes')).toBe('class')
|
|
105
|
+
expect(englishSingular('boxes')).toBe('box')
|
|
106
|
+
expect(englishSingular('batches')).toBe('batch')
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('leaves nouns with no plural ending alone', () => {
|
|
110
|
+
expect(englishSingular('cluster')).toBe('cluster')
|
|
111
|
+
expect(englishSingular('policy')).toBe('policy')
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
// The `-es` strip over-shortens a `*se` singular ('leases' → 'leas', not
|
|
115
|
+
// 'lease'), but englishPlural's `s → +es` rule inverts it exactly, so the
|
|
116
|
+
// round trip these two functions are used for still holds. Only the round
|
|
117
|
+
// trip is contractual — the intermediate stem is not.
|
|
118
|
+
it('over-strips *se stems but stays round-trip exact', () => {
|
|
119
|
+
expect(englishSingular('leases')).toBe('leas')
|
|
120
|
+
expect(englishPlural('leas')).toBe('leases')
|
|
121
|
+
expect(englishSingular('helmreleases')).toBe('helmreleas')
|
|
122
|
+
expect(englishPlural('helmreleas')).toBe('helmreleases')
|
|
123
|
+
})
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
describe('isEnglishPlural', () => {
|
|
127
|
+
it('recognizes regular plurals', () => {
|
|
128
|
+
expect(isEnglishPlural('schedules')).toBe(true)
|
|
129
|
+
expect(isEnglishPlural('validatingpolicies')).toBe(true)
|
|
130
|
+
expect(isEnglishPlural('virtualservices')).toBe(true)
|
|
131
|
+
expect(isEnglishPlural('gatewayclasses')).toBe(true)
|
|
132
|
+
expect(isEnglishPlural('endpoints')).toBe(true)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
// Plurals whose singular ends in `se`. The stem englishSingular produces is
|
|
136
|
+
// wrong, but the round trip is not — see the englishSingular case above.
|
|
137
|
+
it('recognizes plurals of *se singulars', () => {
|
|
138
|
+
expect(isEnglishPlural('leases')).toBe(true)
|
|
139
|
+
expect(isEnglishPlural('helmreleases')).toBe(true)
|
|
140
|
+
expect(isEnglishPlural('releases')).toBe(true)
|
|
141
|
+
expect(isEnglishPlural('licenses')).toBe(true)
|
|
142
|
+
expect(isEnglishPlural('databases')).toBe(true)
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
// KNOWN LIMIT. A singular ending in a lone `s` whose stem doesn't end in
|
|
146
|
+
// s/x/ch/sh is indistinguishable from a plural by English rules alone
|
|
147
|
+
// ('status' → 'statu' → 'status' round-trips exactly like 'clusters' →
|
|
148
|
+
// 'cluster' → 'clusters'). No Kubernetes Kind Radar handles has this shape,
|
|
149
|
+
// and kindToPlural's discovered-map lookup takes over the moment discovery
|
|
150
|
+
// lands, so the mistake is confined to the cold window. Pinned so a future
|
|
151
|
+
// change to these rules surfaces the boundary rather than moving it silently.
|
|
152
|
+
it('cannot distinguish singulars ending in a lone s', () => {
|
|
153
|
+
expect(isEnglishPlural('status')).toBe(true)
|
|
154
|
+
expect(isEnglishPlural('alias')).toBe(true)
|
|
155
|
+
expect(isEnglishPlural('canvas')).toBe(true)
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
// The reason this predicate exists rather than a bare endsWith('s'): these
|
|
159
|
+
// are singular Kubernetes kinds that happen to end in 's'.
|
|
160
|
+
it('rejects singulars that merely end in s', () => {
|
|
161
|
+
expect(isEnglishPlural('ingress')).toBe(false)
|
|
162
|
+
expect(isEnglishPlural('nodeclass')).toBe(false)
|
|
163
|
+
expect(isEnglishPlural('ec2nodeclass')).toBe(false)
|
|
164
|
+
expect(isEnglishPlural('storageclass')).toBe(false)
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
it('rejects singulars with no plural ending', () => {
|
|
168
|
+
expect(isEnglishPlural('cluster')).toBe(false)
|
|
169
|
+
expect(isEnglishPlural('validatingpolicy')).toBe(false)
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it('agrees with englishPlural on every round trip', () => {
|
|
173
|
+
for (const singular of ['cluster', 'policy', 'class', 'box', 'batch', 'schedule', 'service']) {
|
|
174
|
+
expect(isEnglishPlural(englishPlural(singular))).toBe(true)
|
|
175
|
+
}
|
|
176
|
+
})
|
|
177
|
+
})
|
package/src/utils/pluralize.ts
CHANGED
|
@@ -30,6 +30,40 @@ export function englishPlural(noun: string): string {
|
|
|
30
30
|
return noun + 's';
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Undo englishPlural's regular rules.
|
|
35
|
+
*
|
|
36
|
+
* englishSingular('classes') → 'class'
|
|
37
|
+
* englishSingular('policies') → 'policy'
|
|
38
|
+
* englishSingular('clusters') → 'cluster'
|
|
39
|
+
*
|
|
40
|
+
* Lossy by construction — a noun that is already singular comes back mangled
|
|
41
|
+
* ('ingress' → 'ingres'). Use isEnglishPlural to find out which case you have.
|
|
42
|
+
*/
|
|
43
|
+
export function englishSingular(noun: string): string {
|
|
44
|
+
const lower = noun.toLowerCase();
|
|
45
|
+
if (lower.endsWith('ies')) return noun.slice(0, -3) + 'y';
|
|
46
|
+
if (lower.endsWith('ses') || lower.endsWith('xes') || lower.endsWith('ches') || lower.endsWith('shes')) {
|
|
47
|
+
return noun.slice(0, -2);
|
|
48
|
+
}
|
|
49
|
+
if (lower.endsWith('s')) return noun.slice(0, -1);
|
|
50
|
+
return noun;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Whether a noun is already the regular English plural of its own singular —
|
|
55
|
+
* i.e. whether englishPlural would reproduce it. Distinguishes a plural from a
|
|
56
|
+
* singular that merely ends in 's':
|
|
57
|
+
*
|
|
58
|
+
* isEnglishPlural('schedules') → true ('schedule' → 'schedules')
|
|
59
|
+
* isEnglishPlural('policies') → true ('policy' → 'policies')
|
|
60
|
+
* isEnglishPlural('ingress') → false ('ingres' → 'ingreses')
|
|
61
|
+
* isEnglishPlural('nodeclass') → false ('nodeclas' → 'nodeclases')
|
|
62
|
+
*/
|
|
63
|
+
export function isEnglishPlural(noun: string): boolean {
|
|
64
|
+
return englishPlural(englishSingular(noun)) === noun;
|
|
65
|
+
}
|
|
66
|
+
|
|
33
67
|
/**
|
|
34
68
|
* Count-aware plural noun. Returns the singular when n===1, else the
|
|
35
69
|
* plural form (passed-in or derived via englishPlural).
|