Skip to content

Commit 4cb4c1e

Browse files
committed
schema-agnostic approaches with working tests
Signed-off-by: Jordan <jordan@nimblewidget.com>
1 parent 3c7e13b commit 4cb4c1e

20 files changed

Lines changed: 1686 additions & 684 deletions

cmd/catalogd/main.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,10 +365,25 @@ func run(ctx context.Context) error {
365365
return err
366366
}
367367

368+
var metasMode storage.MetasHandlerMode
369+
if features.CatalogdFeatureGate.Enabled(features.APIV1MetasHandler) {
370+
metasMode = storage.MetasHandlerEnabled
371+
} else {
372+
metasMode = storage.MetasHandlerDisabled
373+
}
374+
375+
var graphqlMode storage.GraphQLQueriesMode
376+
if features.CatalogdFeatureGate.Enabled(features.GraphQLCatalogQueries) {
377+
graphqlMode = storage.GraphQLQueriesEnabled
378+
} else {
379+
graphqlMode = storage.GraphQLQueriesDisabled
380+
}
381+
368382
localStorage = storage.NewLocalDirV1(
369383
storeDir,
370384
baseStorageURL,
371-
features.CatalogdFeatureGate.Enabled(features.APIV1MetasHandler),
385+
metasMode,
386+
graphqlMode,
372387
)
373388

374389
// Config for the catalogd web server
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
# Catalog queries using GraphQL
2+
3+
!!! warning "Alpha Feature"
4+
The GraphQL endpoint is an **alpha feature** controlled by the `GraphQLCatalogQueries` feature gate.
5+
The API and behavior may change in future releases.
6+
7+
After you [add a catalog of extensions](../../tutorials/add-catalog.md) to your cluster, you can query the catalog using GraphQL for flexible, structured queries with precise field selection.
8+
9+
## Prerequisites
10+
11+
* You have added a ClusterCatalog of extensions, such as [OperatorHub.io](https://operatorhub.io), to your cluster.
12+
* The `GraphQLCatalogQueries` feature gate is enabled in catalogd.
13+
14+
!!! note
15+
By default, Catalogd is installed with TLS enabled for the catalog webserver.
16+
The following examples will show this default behavior, but for simplicity's sake will ignore TLS verification in the curl commands using the `-k` flag.
17+
18+
You also need to port forward the catalog server service:
19+
20+
``` terminal
21+
kubectl -n olmv1-system port-forward svc/catalogd-service 8443:443
22+
```
23+
24+
## GraphQL Endpoint
25+
26+
The GraphQL endpoint is available at:
27+
28+
```
29+
https://localhost:8443/catalogs/<catalog-name>/api/v1/graphql
30+
```
31+
32+
All queries must be sent as **HTTP POST** requests with a JSON body containing a `query` field.
33+
34+
## Understanding GraphQL Field Names
35+
36+
**IMPORTANT**: GraphQL field names are automatically generated from catalog schema names.
37+
38+
### Naming Convention
39+
40+
Schema names are converted to GraphQL field names using this process:
41+
42+
1. Remove dots and special characters: `olm.bundle``olmbundle`
43+
2. Convert to lowercase: `OLM.Bundle``olmbundle`
44+
3. Append 's' for pluralization: `olmbundle``olmbundles`
45+
46+
**Examples:**
47+
48+
| Schema Name | GraphQL Field Name |
49+
|-------------|-------------------|
50+
| `olm.bundle` | `olmbundles` |
51+
| `olm.package` | `olmpackages` |
52+
| `olm.channel` | `olmchannels` |
53+
| `helm.chart` | `helmcharts` |
54+
55+
### Discovering Available Fields
56+
57+
To find the exact field names available for your catalog, use GraphQL introspection:
58+
59+
``` terminal
60+
curl -k -X POST 'https://localhost:8443/catalogs/operatorhubio/api/v1/graphql' \
61+
-H "Content-Type: application/json" \
62+
-d '{
63+
"query": "{ __schema { queryType { fields { name description } } } }"
64+
}' | jq
65+
```
66+
67+
This returns all available query fields for the catalog, including the automatically generated schema-based fields.
68+
69+
!!! warning "Pluralization Limitations"
70+
The current implementation appends 's' to schema names for pluralization. This may not produce grammatically correct English plurals in all cases (e.g., `index``indexs` instead of `indices`). When creating custom schemas, use singular nouns that pluralize well with a simple 's' suffix.
71+
72+
## Basic Queries
73+
74+
### Catalog Summary
75+
76+
Get an overview of schemas and object counts in the catalog:
77+
78+
``` terminal
79+
curl -k -X POST 'https://localhost:8443/catalogs/operatorhubio/api/v1/graphql' \
80+
-H "Content-Type: application/json" \
81+
-d '{
82+
"query": "{ summary { totalSchemas schemas { name totalObjects totalFields } } }"
83+
}' | jq
84+
```
85+
86+
### Query Bundles
87+
88+
List bundles with specific fields:
89+
90+
``` terminal
91+
curl -k -X POST 'https://localhost:8443/catalogs/operatorhubio/api/v1/graphql' \
92+
-H "Content-Type: application/json" \
93+
-d '{
94+
"query": "{ olmbundles(limit: 5, offset: 0) { name package image } }"
95+
}' | jq
96+
```
97+
98+
### Query Packages
99+
100+
List packages with metadata:
101+
102+
``` terminal
103+
curl -k -X POST 'https://localhost:8443/catalogs/operatorhubio/api/v1/graphql' \
104+
-H "Content-Type: application/json" \
105+
-d '{
106+
"query": "{ olmpackages(limit: 10) { name description defaultChannel } }"
107+
}' | jq
108+
```
109+
110+
### Query Channels
111+
112+
List channels:
113+
114+
``` terminal
115+
curl -k -X POST 'https://localhost:8443/catalogs/operatorhubio/api/v1/graphql' \
116+
-H "Content-Type: application/json" \
117+
-d '{
118+
"query": "{ olmchannels { name package entries } }"
119+
}' | jq
120+
```
121+
122+
## Advanced Queries
123+
124+
### Pagination
125+
126+
All schema-based queries support pagination via `limit` and `offset` arguments:
127+
128+
``` terminal
129+
curl -k -X POST 'https://localhost:8443/catalogs/operatorhubio/api/v1/graphql' \
130+
-H "Content-Type: application/json" \
131+
-d '{
132+
"query": "{ olmbundles(limit: 10, offset: 20) { name } }"
133+
}' | jq
134+
```
135+
136+
### Nested Field Selection
137+
138+
Select only the fields you need, including nested objects:
139+
140+
``` terminal
141+
curl -k -X POST 'https://localhost:8443/catalogs/operatorhubio/api/v1/graphql' \
142+
-H "Content-Type: application/json" \
143+
-d '{
144+
"query": "{ olmpackages { name icon { mediatype base64data } } }"
145+
}' | jq
146+
```
147+
148+
### Complex Bundle Properties
149+
150+
Query bundle properties with their type and value fields:
151+
152+
``` terminal
153+
curl -k -X POST 'https://localhost:8443/catalogs/operatorhubio/api/v1/graphql' \
154+
-H "Content-Type: application/json" \
155+
-d '{
156+
"query": "{ olmbundles(limit: 5) { name properties { type value } } }"
157+
}' | jq
158+
```
159+
160+
**Note:** The `properties` field contains an array of objects, each with a `type` string and a `value` field that can contain complex nested data. GraphQL will return the full JSON structure for the `value` field.
161+
162+
## Comparing GraphQL vs Metas Endpoint
163+
164+
| Feature | GraphQL (`/api/v1/graphql`) | Metas (`/api/v1/metas`) |
165+
|---------|---------------------------|------------------------|
166+
| Field selection | Precise - request only needed fields | All fields always returned |
167+
| Query complexity | Rich queries with nested objects | Simple parameter-based filtering |
168+
| Response size | Minimal - only requested data | Full objects always returned |
169+
| Schema discovery | Introspection built-in | External documentation needed |
170+
| Pagination | Built-in `limit` and `offset` | Manual implementation required |
171+
| HTTP Method | POST only | GET supported |
172+
| Feature status | Alpha (feature gate required) | Stable |
173+
174+
**When to use GraphQL:**
175+
- You need specific fields from large objects
176+
- You want to query related data in a single request
177+
- You need structured, typed responses
178+
- You're building a UI or client that benefits from precise data fetching
179+
180+
**When to use Metas endpoint:**
181+
- You need simple, stable API
182+
- You're doing basic filtering by schema/package/name
183+
- You want to use GET requests for caching
184+
- You need guaranteed API stability
185+
186+
## Limitations
187+
188+
1. **Pluralization**: Schema names are pluralized by appending 's', which may not be grammatically correct for all words
189+
2. **Schema naming**: Full schema names (including namespace/prefix) are preserved in field names (`olm.bundle``olmbundles`, not `bundles`)
190+
3. **POST only**: GraphQL endpoint only accepts POST requests, unlike the metas endpoint which supports GET
191+
4. **Alpha stability**: API may change in future releases while in alpha
192+
193+
## Enabling the GraphQL Feature
194+
195+
The GraphQL endpoint is controlled by the `GraphQLCatalogQueries` feature gate. To enable it:
196+
197+
``` yaml
198+
args:
199+
- --feature-gates=GraphQLCatalogQueries=true
200+
```
201+
202+
See [enable webhook support](enable-webhook-support.md) for more details on configuring feature gates.

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ require (
1717
github.com/google/go-containerregistry v0.21.4
1818
github.com/google/renameio/v2 v2.0.2
1919
github.com/gorilla/handlers v1.5.2
20+
github.com/graphql-go/graphql v0.8.1
2021
github.com/klauspost/compress v1.18.5
2122
github.com/opencontainers/go-digest v1.0.0
2223
github.com/opencontainers/image-spec v1.1.1
@@ -144,7 +145,6 @@ require (
144145
github.com/gorilla/mux v1.8.1 // indirect
145146
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
146147
github.com/gosuri/uitable v0.0.4 // indirect
147-
github.com/graphql-go/graphql v0.8.1 // indirect
148148
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
149149
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect
150150
github.com/h2non/filetype v1.1.3 // indirect

helm/experimental.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@ options:
2323
features:
2424
enabled:
2525
- APIV1MetasHandler
26+
- GraphQLCatalogQueries
2627
# This can be one of: standard or experimental
2728
featureSet: experimental

internal/catalogd/features/features.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ import (
99
)
1010

1111
const (
12-
APIV1MetasHandler = featuregate.Feature("APIV1MetasHandler")
12+
APIV1MetasHandler = featuregate.Feature("APIV1MetasHandler")
1313
GraphQLCatalogQueries = featuregate.Feature("GraphQLCatalogQueries")
1414
)
1515

1616
var catalogdFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
17-
APIV1MetasHandler: {Default: false, PreRelease: featuregate.Alpha},
18-
GraphQLCatalogQueries: {Default: false, PreRelease: featuregate.Alpha},
17+
APIV1MetasHandler: {Default: false, PreRelease: featuregate.Alpha, LockToDefault: false},
18+
GraphQLCatalogQueries: {Default: false, PreRelease: featuregate.Alpha, LockToDefault: false},
1919
}
2020

2121
var CatalogdFeatureGate featuregate.MutableFeatureGate = featuregate.NewFeatureGate()

0 commit comments

Comments
 (0)