Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
a83263f
Adds robust cursor-based pagination with API page size config
InduwaraSMPN Dec 14, 2025
0b6f7bd
Adds scalable incremental ingestion for OpenChoreo
InduwaraSMPN Dec 14, 2025
302ed37
Disables transactions for concurrent index creation
InduwaraSMPN Dec 15, 2025
8b157ba
Clarifies cursor iteration semantics with comments
InduwaraSMPN Dec 15, 2025
585526d
Refines tag generation in template converter tests
InduwaraSMPN Dec 15, 2025
f3d884b
Updates tests for revised CI/CD setup and cursor handling
InduwaraSMPN Dec 15, 2025
43cbf12
Improves code formatting and enhances error handling logs
InduwaraSMPN Dec 15, 2025
12ec81c
Adds immediate catalog incremental provider for OpenChoreo
InduwaraSMPN Dec 15, 2025
86e15ec
Updates page limit to use shared constant
InduwaraSMPN Dec 15, 2025
d09b1dc
Updates default page limit to 512 for API alignment
InduwaraSMPN Dec 15, 2025
6a4bff5
Updates max pagination limit to 512
InduwaraSMPN Dec 15, 2025
76a63bf
Mock response headers for content-length in tests
InduwaraSMPN Dec 15, 2025
2198f72
Adds robust cursor-based pagination with API page size config
InduwaraSMPN Dec 14, 2025
4edf1ec
Adds scalable incremental ingestion for OpenChoreo
InduwaraSMPN Dec 14, 2025
9c93089
Disables transactions for concurrent index creation
InduwaraSMPN Dec 15, 2025
ad5f286
Clarifies cursor iteration semantics with comments
InduwaraSMPN Dec 15, 2025
bbd4d15
Refines tag generation in template converter tests
InduwaraSMPN Dec 15, 2025
1c2c79c
Updates tests for revised CI/CD setup and cursor handling
InduwaraSMPN Dec 15, 2025
b46d0d8
Improves code formatting and enhances error handling logs
InduwaraSMPN Dec 15, 2025
34f4053
Adds immediate catalog incremental provider for OpenChoreo
InduwaraSMPN Dec 15, 2025
06cc14e
Updates page limit to use shared constant
InduwaraSMPN Dec 15, 2025
80eace1
Updates default page limit to 512 for API alignment
InduwaraSMPN Dec 15, 2025
cbb53c4
Updates max pagination limit to 512
InduwaraSMPN Dec 15, 2025
9ca2c9c
Mock response headers for content-length in tests
InduwaraSMPN Dec 15, 2025
ac0398a
Merge branch 'main' of https://github.com/InduwaraSMPN/backstage-plugins
InduwaraSMPN Dec 15, 2025
86686ce
Removes redundant error handling and dead code blocks
InduwaraSMPN Dec 15, 2025
0cfaa20
Merge branch 'main' of https://github.com/openchoreo/backstage-plugins
InduwaraSMPN Dec 15, 2025
437f7ef
Adds paginated trait loading and improves incremental config
InduwaraSMPN Dec 15, 2025
e3290f9
Apply prettier:write
InduwaraSMPN Dec 16, 2025
b4fa24a
Adds timeout and abort support to pagination helper
InduwaraSMPN Dec 16, 2025
6a79923
Clarifies incremental ingestion setup steps
InduwaraSMPN Dec 16, 2025
968e939
refactor(pagination): reflow long lines for readability
InduwaraSMPN Dec 17, 2025
4f33a77
docs(openapi): clarify limit query param description in OpenAPI spec
InduwaraSMPN Dec 17, 2025
0b428ab
refactor(comments): collapse incremental ingestion step 2/3 into step…
InduwaraSMPN Dec 17, 2025
ed30382
docs: add comments and configuration for incremental ingestion
InduwaraSMPN Dec 17, 2025
f2a96fe
chore(deps): update yarn.lock
InduwaraSMPN Dec 17, 2025
6530e58
test: adjust test timeouts, skip in CI, and update expected outputs
InduwaraSMPN Dec 17, 2025
3449126
refactor(config): move zod schemas from .d.ts to .ts implementation file
InduwaraSMPN Dec 17, 2025
0d35e31
Merge remote-tracking branch 'upstream/main'
InduwaraSMPN Dec 18, 2025
6639a66
Merge remote-tracking branch 'upstream/main'
InduwaraSMPN Dec 19, 2025
79bfbdd
Merge remote-tracking branch 'upstream/main'
InduwaraSMPN Dec 19, 2025
79b48a6
Merge remote-tracking branch 'upstream/main'
InduwaraSMPN Dec 20, 2025
779f814
Merge remote-tracking branch 'upstream/main'
InduwaraSMPN Dec 21, 2025
20627fd
Merge remote-tracking branch 'upstream/main'
InduwaraSMPN Dec 21, 2025
5cfc72d
Merge remote-tracking branch 'upstream/main'
InduwaraSMPN Dec 21, 2025
24ffb9d
Merge remote-tracking branch 'upstream/main'
InduwaraSMPN Dec 22, 2025
7c39894
Merge remote-tracking branch 'upstream/main'
InduwaraSMPN Dec 22, 2025
b56430c
chore: update dependencies to latest versions
InduwaraSMPN Dec 22, 2025
cde1e78
Merge remote-tracking branch 'upstream/main'
InduwaraSMPN Dec 22, 2025
9a98da1
Merge remote-tracking branch 'upstream/main'
InduwaraSMPN Dec 23, 2025
cdf18b0
Merge remote-tracking branch 'upstream/main'
InduwaraSMPN Dec 23, 2025
b98f29a
Updates incremental ingestion step count to two
InduwaraSMPN Dec 23, 2025
1d40760
Merge remote-tracking branch 'upstream/main'
InduwaraSMPN Dec 23, 2025
78c59c3
Merge remote-tracking branch 'upstream/main'
InduwaraSMPN Dec 23, 2025
af6735d
Merge remote-tracking branch 'upstream/main'
InduwaraSMPN Dec 25, 2025
8cfcfd7
Merge remote-tracking branch 'upstream/main'
InduwaraSMPN Jan 6, 2026
6011c82
Merge remote-tracking branch 'upstream/main'
InduwaraSMPN Jan 7, 2026
be8c575
Merge remote-tracking branch 'upstream/main'
InduwaraSMPN Jan 12, 2026
c40cc43
Merge remote-tracking branch 'upstream/main'
InduwaraSMPN Jan 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions app-config.local.yaml.example
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ backend:
openchoreo:
baseUrl: http://api.openchoreo.localhost:8080/api/v1
# token: "" # Optional: uncomment if you need API authentication
# OPTIONAL: For large-scale local testing, enable incremental ingestion
# incremental:
# burstLength: 16 # Duration of each burst of processing activity in seconds
# burstInterval: 8 # Interval between bursts of processing activity in seconds
# chunkSize: 512 # Number of items to fetch per API request
# restLength: 60 # Duration of rest periods between bursts in minutes

# OAuth2 Client Credentials for background tasks (Catalog Entity Provider)
# Required for the Catalog Provider to fetch organizations, projects, and components
Expand Down
9 changes: 9 additions & 0 deletions app-config.production.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,18 @@ openchoreo:
# scopes: ['openid'] # Optional: uncomment to specify scopes

defaultOwner: 'platformengineer' # Default owner for catalog entities
# DEFAULT: Standard scheduled ingestion (recommended for most deployments)
schedule:
frequency: 30 # seconds between runs (default: 30)
timeout: 120 # seconds for timeout (default: 120)
# OPTIONAL: For large-scale deployments, use incremental ingestion instead
# Uncomment the section below and comment out the schedule section above
# Also update packages/backend/src/index.ts to use the incremental module
# incremental:
# burstLength: 16 # Duration of each burst of processing activity in seconds
# burstInterval: 8 # Interval between bursts of processing activity in seconds
# chunkSize: 512 # Number of items to fetch per API request
# restLength: 60 # Duration of rest periods between bursts in minutes

# Feature flags for enabling/disabling OpenChoreo functionality
# Environment variables: OPENCHOREO_FEATURES_WORKFLOWS_ENABLED, OPENCHOREO_FEATURES_OBSERVABILITY_ENABLED, OPENCHOREO_FEATURES_AUTH_ENABLED
Expand Down
10 changes: 10 additions & 0 deletions app-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,19 @@ openchoreo:
# scopes: ['openid'] # Optional: uncomment to specify scopes

defaultOwner: 'platformengineer' # Default owner for catalog entities

# DEFAULT: Standard scheduled ingestion (recommended for most deployments)
schedule:
frequency: 30 # seconds between runs (default: 30)
timeout: 120 # seconds for timeout (default: 120)
# OPTIONAL: For large-scale deployments, use incremental ingestion instead
# Uncomment the section below and comment out the schedule section above
# Also update packages/backend/src/index.ts to use the incremental module
# incremental:
# burstLength: 16 # Duration of each burst of processing activity in seconds
# burstInterval: 8 # Interval between bursts of processing activity in seconds
# chunkSize: 512 # Number of items to fetch per API request
# restLength: 60 # Duration of rest periods between bursts in minutes

# Feature flags for enabling/disabling OpenChoreo functionality
# These can be controlled via Helm values: backstage.features.*
Expand Down
113 changes: 105 additions & 8 deletions packages/app/src/scaffolder/TraitsField/TraitsFieldExtension.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,13 @@ export const TraitsField = ({
const [addedTraits, setAddedTraits] = useState<AddedTrait[]>(formData || []);
const [selectedTrait, setSelectedTrait] = useState<string>('');
const [loadingTraits, setLoadingTraits] = useState(false);
const [loadingMoreTraits, setLoadingMoreTraits] = useState(false);
const [loadingSchema, setLoadingSchema] = useState(false);
const [error, setError] = useState<string | null>(null);
const [hasMoreTraits, setHasMoreTraits] = useState(true);
const [continueToken, setContinueToken] = useState<string | undefined>(
undefined,
);

const discoveryApi = useApi(discoveryApiRef);
const fetchApi = useApi(fetchApiRef);
Expand All @@ -89,12 +94,16 @@ export const TraitsField = ({
useEffect(() => {
let ignore = false;

const fetchTraits = async () => {
const fetchTraits = async (cursor?: string, append = false) => {
if (!organizationName) {
return;
}

setLoadingTraits(true);
if (!append) {
setLoadingTraits(true);
} else {
setLoadingMoreTraits(true);
}
setError(null);

try {
Expand All @@ -108,12 +117,17 @@ export const TraitsField = ({

const orgName = extractOrgName(organizationName);

// Build URL with pagination parameters
const url = new URL(`${baseUrl}/traits`);
url.searchParams.set('organizationName', orgName);
url.searchParams.set('limit', '100'); // Reasonable page size for UI

if (cursor) {
url.searchParams.set('continue', cursor);
}

// Use fetchApi which automatically injects Backstage + IDP tokens
const response = await fetchApi.fetch(
`${baseUrl}/traits?organizationName=${encodeURIComponent(
orgName,
)}&page=1&pageSize=100`,
);
const response = await fetchApi.fetch(url.toString());

if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
Expand All @@ -122,7 +136,18 @@ export const TraitsField = ({
const result = await response.json();

if (!ignore && result.success) {
setAvailableTraits(result.data.items);
const newTraits = result.data.items || [];
const metadata = result.data.metadata;

if (append) {
setAvailableTraits(prev => [...prev, ...newTraits]);
} else {
setAvailableTraits(newTraits);
}

// Update pagination state
setHasMoreTraits(metadata?.hasMore === true);
setContinueToken(metadata?.continue);
}
} catch (err) {
if (!ignore) {
Expand All @@ -131,17 +156,71 @@ export const TraitsField = ({
} finally {
if (!ignore) {
setLoadingTraits(false);
setLoadingMoreTraits(false);
}
}
};

// Reset pagination state when organization changes
setAvailableTraits([]);
setHasMoreTraits(true);
setContinueToken(undefined);

fetchTraits();

return () => {
ignore = true;
};
}, [organizationName, discoveryApi, fetchApi]);

// Load more traits
const handleLoadMoreTraits = () => {
if (continueToken && !loadingMoreTraits) {
// We need to recreate the fetchTraits function here since it's defined in useEffect
const loadMore = async () => {
if (!organizationName) return;

setLoadingMoreTraits(true);
setError(null);

try {
const baseUrl = await discoveryApi.getBaseUrl('openchoreo');
const extractOrgName = (fullOrgName: string): string => {
const parts = fullOrgName.split('/');
return parts[parts.length - 1];
};
const orgName = extractOrgName(organizationName);

const url = new URL(`${baseUrl}/traits`);
url.searchParams.set('organizationName', orgName);
url.searchParams.set('limit', '100');
url.searchParams.set('continue', continueToken);

const response = await fetchApi.fetch(url.toString());
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}

const result = await response.json();
if (result.success) {
const newTraits = result.data.items || [];
const metadata = result.data.metadata;

setAvailableTraits(prev => [...prev, ...newTraits]);
setHasMoreTraits(metadata?.hasMore === true);
setContinueToken(metadata?.continue);
}
} catch (err) {
setError(`Failed to load more traits: ${err}`);
} finally {
setLoadingMoreTraits(false);
}
};

loadMore();
}
};

// Fetch schema for selected trait and add it
const handleAddTrait = async () => {
if (!selectedTrait || !organizationName) {
Expand Down Expand Up @@ -273,6 +352,24 @@ export const TraitsField = ({
{trait.name}
</MenuItem>
))}

{/* Load More Button */}
{!loadingTraits && hasMoreTraits && (
<MenuItem
disabled={loadingMoreTraits}
onClick={handleLoadMoreTraits}
style={{ justifyContent: 'center', fontStyle: 'italic' }}
>
{loadingMoreTraits ? (
<>
<CircularProgress size={20} style={{ marginRight: 8 }} />
Loading more traits...
</>
) : (
'Load more traits...'
)}
</MenuItem>
)}
</Select>
</FormControl>
<Button
Expand Down
1 change: 1 addition & 0 deletions packages/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"@openchoreo/backstage-plugin-platform-engineer-core-backend": "workspace:^",
"@openchoreo/backstage-plugin-scaffolder-backend-module": "workspace:^",
"@openchoreo/openchoreo-auth": "workspace:^",
"@openchoreo/plugin-catalog-backend-module-openchoreo-incremental": "workspace:^",
"app": "link:../app",
"better-sqlite3": "9.6.0",
"cookie-parser": "1.4.7",
Expand Down
25 changes: 25 additions & 0 deletions packages/backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ import { rootHttpRouterServiceFactory } from '@backstage/backend-defaults/rootHt
import { immediateCatalogServiceFactory } from '@openchoreo/backstage-plugin-catalog-backend-module';
import { createIdpTokenHeaderMiddleware } from '@openchoreo/openchoreo-auth';

/**
* OPTIONAL: For large-scale deployments, use the incremental ingestion module
*
* ----------------------------------------------------------------------
* INCREMENTAL INGESTION: STEP 1 of 2
* ----------------------------------------------------------------------
*/
// UNCOMMENT this import line below.
// import { catalogModuleOpenchoreoIncrementalProvider } from '@openchoreo/plugin-catalog-backend-module-openchoreo-incremental';

const backend = createBackend();

// Configure root HTTP router with IDP token header middleware
Expand Down Expand Up @@ -60,6 +70,21 @@ backend.add(
import('@backstage/plugin-catalog-backend-module-scaffolder-entity-model'),
);

/**
* ----------------------------------------------------------------------
* INCREMENTAL INGESTION: STEP 2 of 2
* ----------------------------------------------------------------------
*
* Note: You must also update app-config.yaml to use:
* 'openchoreo.incremental' instead of 'openchoreo.schedule'
*/

// UNCOMMENT the block below..
// backend.add(
// import('@openchoreo/plugin-catalog-backend-module-openchoreo-incremental'),
// );
// backend.add(catalogModuleOpenchoreoIncrementalProvider);

// See https://backstage.io/docs/features/software-catalog/configuration#subscribing-to-catalog-errors
backend.add(import('@backstage/plugin-catalog-backend-module-logs'));

Expand Down
Loading
Loading