Skip to content

Commit 25aea21

Browse files
authored
fix(cli): remove datasources from components push command (#236)
Components push command no longer attempts to manage datasources, resolving failed datasource operations during component synchronisation. - Remove datasources fetching and processing from components push - Remove datasource stub creation and dependency resolution - Add informational messaging directing users to dedicated datasources command - Components referencing datasources will work if datasources exist in target space Fixes: WDX-68
1 parent a2267eb commit 25aea21

File tree

4 files changed

+37
-291
lines changed

4 files changed

+37
-291
lines changed

packages/cli/src/commands/components/push/graph-operations/__tests__/graph-integration.test.ts

Lines changed: 1 addition & 194 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { beforeEach, describe, expect, it, vi } from 'vitest';
2-
import { buildDependencyGraph, collectWhitelistDependencies, determineProcessingOrder, validateGraph } from '../dependency-graph';
2+
import { buildDependencyGraph, determineProcessingOrder, validateGraph } from '../dependency-graph';
33
import { processAllResources } from '../resource-processor';
44
import type { SpaceComponentsDataState } from '../../../constants';
55

@@ -753,197 +753,4 @@ describe('graph Integration Tests', () => {
753753
expect(processingOrder.length).toBeGreaterThan(0);
754754
});
755755
});
756-
757-
describe('datasource Dependencies', () => {
758-
it('should detect datasource dependencies in component schemas', () => {
759-
const schema = {
760-
option_field: {
761-
type: 'option',
762-
source: 'internal',
763-
datasource_slug: 'colors',
764-
},
765-
options_field: {
766-
type: 'options',
767-
source: 'internal',
768-
datasource_slug: 'categories',
769-
},
770-
external_option: {
771-
type: 'option',
772-
source: 'external',
773-
url: 'https://api.example.com/data',
774-
},
775-
};
776-
777-
const dependencies = collectWhitelistDependencies(schema);
778-
779-
expect(dependencies.datasourceNames.has('colors')).toBe(true);
780-
expect(dependencies.datasourceNames.has('categories')).toBe(true);
781-
expect(dependencies.datasourceNames.size).toBe(2);
782-
});
783-
784-
it('should build dependency graph with datasources from component references', () => {
785-
const mockSpaceState: SpaceComponentsDataState = {
786-
local: {
787-
components: [
788-
{
789-
id: 1,
790-
name: 'ProductCard',
791-
display_name: 'Product Card',
792-
schema: {
793-
category: {
794-
type: 'option',
795-
source: 'internal',
796-
datasource_slug: 'categories',
797-
},
798-
},
799-
created_at: new Date().toISOString(),
800-
updated_at: new Date().toISOString(),
801-
space_id: 123,
802-
},
803-
],
804-
groups: [],
805-
presets: [],
806-
internalTags: [],
807-
datasources: [], // No local datasources - stubs created from component references
808-
},
809-
target: {
810-
components: new Map(),
811-
groups: new Map(),
812-
tags: new Map(),
813-
presets: new Map(),
814-
datasources: new Map(),
815-
},
816-
};
817-
818-
const graph = buildDependencyGraph({ spaceState: mockSpaceState });
819-
820-
expect(graph.nodes.has('component:ProductCard')).toBe(true);
821-
expect(graph.nodes.has('datasource:categories')).toBe(true);
822-
823-
const component = graph.nodes.get('component:ProductCard');
824-
const datasource = graph.nodes.get('datasource:categories');
825-
826-
expect(component?.dependencies.has('datasource:categories')).toBe(true);
827-
expect(datasource?.dependents.has('component:ProductCard')).toBe(true);
828-
});
829-
830-
it('should create stub datasources for referenced datasources', () => {
831-
const mockSpaceState: SpaceComponentsDataState = {
832-
local: {
833-
components: [
834-
{
835-
id: 1,
836-
name: 'ProductCard',
837-
display_name: 'Product Card',
838-
schema: {
839-
category: {
840-
type: 'option',
841-
source: 'internal',
842-
datasource_slug: 'referenced_datasource',
843-
},
844-
},
845-
created_at: new Date().toISOString(),
846-
updated_at: new Date().toISOString(),
847-
space_id: 123,
848-
},
849-
],
850-
groups: [],
851-
presets: [],
852-
internalTags: [],
853-
datasources: [], // No datasources in local workspace
854-
},
855-
target: {
856-
components: new Map(),
857-
groups: new Map(),
858-
tags: new Map(),
859-
presets: new Map(),
860-
datasources: new Map(),
861-
},
862-
};
863-
864-
const graph = buildDependencyGraph({ spaceState: mockSpaceState });
865-
866-
expect(graph.nodes.has('component:ProductCard')).toBe(true);
867-
expect(graph.nodes.has('datasource:referenced_datasource')).toBe(true);
868-
869-
const component = graph.nodes.get('component:ProductCard');
870-
const datasource = graph.nodes.get('datasource:referenced_datasource');
871-
872-
expect(component?.dependencies.has('datasource:referenced_datasource')).toBe(true);
873-
expect(datasource?.dependents.has('component:ProductCard')).toBe(true);
874-
875-
// Verify the stub datasource has correct structure
876-
expect(datasource?.sourceData.name).toBe('referenced_datasource');
877-
expect(datasource?.sourceData.slug).toBe('referenced_datasource');
878-
expect(datasource?.sourceData.entries).toEqual([]);
879-
});
880-
881-
it('should resolve datasource references in component schemas', () => {
882-
const mockTargetDatasource = {
883-
id: 42,
884-
name: 'categories',
885-
slug: 'target_categories',
886-
dimensions: [],
887-
entries: [],
888-
created_at: new Date().toISOString(),
889-
updated_at: new Date().toISOString(),
890-
};
891-
892-
const mockSpaceState: SpaceComponentsDataState = {
893-
local: {
894-
components: [
895-
{
896-
id: 1,
897-
name: 'ProductCard',
898-
display_name: 'Product Card',
899-
schema: {
900-
category: {
901-
type: 'option',
902-
source: 'internal',
903-
datasource_slug: 'categories',
904-
},
905-
},
906-
created_at: new Date().toISOString(),
907-
updated_at: new Date().toISOString(),
908-
space_id: 123,
909-
},
910-
],
911-
groups: [],
912-
presets: [],
913-
internalTags: [],
914-
datasources: [
915-
{
916-
id: 1,
917-
name: 'categories',
918-
slug: 'categories',
919-
dimensions: [],
920-
entries: [],
921-
created_at: new Date().toISOString(),
922-
updated_at: new Date().toISOString(),
923-
},
924-
],
925-
},
926-
target: {
927-
components: new Map(),
928-
groups: new Map(),
929-
tags: new Map(),
930-
presets: new Map(),
931-
datasources: new Map([['categories', mockTargetDatasource]]),
932-
},
933-
};
934-
935-
const graph = buildDependencyGraph({ spaceState: mockSpaceState });
936-
937-
// Simulate that the datasource was successfully upserted
938-
const datasourceNode = graph.nodes.get('datasource:categories');
939-
datasourceNode?.updateTargetData(mockTargetDatasource);
940-
941-
const componentNode = graph.nodes.get('component:ProductCard') as ComponentNode;
942-
componentNode.resolveReferences(graph);
943-
944-
// Check that the schema reference was resolved to the target datasource
945-
const resolvedSchema = componentNode.sourceData.schema;
946-
expect(resolvedSchema.category.datasource_slug).toBe('target_categories');
947-
});
948-
});
949756
});

packages/cli/src/commands/components/push/graph-operations/dependency-graph.ts

Lines changed: 7 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,13 @@ import type {
44
SpaceComponentInternalTag,
55
SpaceComponentPreset,
66
} from '../../constants';
7-
import type { SpaceDatasource } from '../../../datasources/constants';
87
import type { DependencyGraph, GraphBuildingContext, NodeData, NodeType, ProcessingLevel, SchemaDependencies, TargetResourceInfo, UnifiedNode } from './types';
98
import { upsertComponent, upsertComponentGroup, upsertComponentInternalTag, upsertComponentPreset } from '../actions';
10-
import { upsertDatasource } from '../../../datasources/push/actions';
119

1210
// =============================================================================
1311
// HELPER FUNCTIONS
1412
// =============================================================================
1513

16-
/**
17-
* Creates a minimal stub datasource with only required fields.
18-
*/
19-
function createStubDatasource(name: string): SpaceDatasource {
20-
return {
21-
id: 0, // Will be set by API
22-
name,
23-
slug: name,
24-
dimensions: [],
25-
entries: [], // Empty entries for stub
26-
created_at: new Date().toISOString(),
27-
updated_at: new Date().toISOString(),
28-
};
29-
}
30-
3114
// =============================================================================
3215
// GRAPH BUILDING
3316
// =============================================================================
@@ -53,27 +36,8 @@ export function buildDependencyGraph(context: GraphBuildingContext): DependencyG
5336
}
5437
}
5538

56-
// Collect all datasource names referenced by components
57-
const referencedDatasources = new Set<string>();
58-
spaceState.local.components.forEach((component) => {
59-
if (component.schema) {
60-
const dependencies = collectWhitelistDependencies(component.schema);
61-
dependencies.datasourceNames.forEach((datasourceName) => {
62-
referencedDatasources.add(datasourceName);
63-
});
64-
}
65-
});
66-
67-
// Create stub datasource nodes for all referenced datasources
68-
referencedDatasources.forEach((datasourceName) => {
69-
const nodeId = `datasource:${datasourceName}`;
70-
const targetDatasource = spaceState.target.datasources?.get(datasourceName);
71-
72-
// Create minimal stub datasource
73-
const stubDatasource = createStubDatasource(datasourceName);
74-
const node = new DatasourceNode(nodeId, stubDatasource, targetDatasource);
75-
graph.nodes.set(nodeId, node);
76-
});
39+
// Note: Datasource dependencies are not processed by the components push command.
40+
// Datasources should be managed using the dedicated 'storyblok datasources push' command.
7741

7842
// Create nodes for all tags with colocated target data
7943
spaceState.local.internalTags.forEach((tag) => {
@@ -185,13 +149,6 @@ export function buildDependencyGraph(context: GraphBuildingContext): DependencyG
185149
const dependencyId = `component:${componentName}`;
186150
addDependency(componentId, dependencyId);
187151
});
188-
189-
// Add dependencies on datasources (from schema fields with internal source)
190-
dependencies.datasourceNames.forEach((datasourceName) => {
191-
const datasourceId = `datasource:${datasourceName}`;
192-
// Always add dependency since we create stubs for all referenced datasources
193-
addDependency(componentId, datasourceId);
194-
});
195152
}
196153
});
197154

@@ -755,17 +712,9 @@ export class ComponentNode extends GraphNode<SpaceComponent> {
755712
// Component whitelist doesn't need ID resolution as it uses names
756713
}
757714

758-
// Resolve datasource references in option/options fields with internal source
759-
if ((resolvedField.type === 'option' || resolvedField.type === 'options') && resolvedField.source === 'internal') {
760-
if (resolvedField.datasource_slug && typeof resolvedField.datasource_slug === 'string') {
761-
const datasourceNodeId = `datasource:${resolvedField.datasource_slug}`;
762-
const datasourceNode = graph.nodes.get(datasourceNodeId) as DatasourceNode;
763-
if (datasourceNode?.targetData) {
764-
// Update the datasource_slug to the target datasource slug
765-
resolvedField.datasource_slug = datasourceNode.targetData.resource.slug;
766-
}
767-
}
768-
}
715+
// Note: Datasource references are not resolved by the components push command.
716+
// Components that reference datasources will work correctly if the datasources
717+
// already exist in the target space with the same slug names.
769718

770719
// Recursively resolve nested fields
771720
Object.keys(resolvedField).forEach((key) => {
@@ -857,28 +806,5 @@ class PresetNode implements UnifiedNode<SpaceComponentPreset> {
857806
}
858807
}
859808

860-
export class DatasourceNode extends GraphNode<SpaceDatasource> {
861-
constructor(id: string, data: SpaceDatasource, targetDatasource?: SpaceDatasource) {
862-
super(id, 'datasource', data.name, data, targetDatasource);
863-
}
864-
865-
resolveReferences(_graph: DependencyGraph): void {
866-
// Datasources don't have references to resolve
867-
}
868-
869-
async upsert(space: string): Promise<SpaceDatasource> {
870-
const existingDatasource = this.targetData?.resource as SpaceDatasource | undefined;
871-
const existingId = existingDatasource?.id;
872-
873-
// For components push, we only create the datasource stub without entries.
874-
// Pushing entries is handled by the datasources push command.
875-
const { entries, ...datasourceDefinition } = this.sourceData;
876-
877-
const result = await upsertDatasource(space, datasourceDefinition, existingId);
878-
if (!result) {
879-
throw new Error(`Failed to upsert datasource ${this.name}`);
880-
}
881-
882-
return result;
883-
}
884-
}
809+
// Note: DatasourceNode class removed as datasources are not managed
810+
// by the components push command. Use 'storyblok datasources push' instead.

packages/cli/src/commands/components/push/graph-operations/types.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,16 @@ import type {
66
SpaceComponentPreset,
77
SpaceComponentsDataState,
88
} from '../../constants';
9-
import type { SpaceDatasource } from '../../../datasources/constants';
109

1110
// =============================================================================
1211
// CORE TYPES
1312
// =============================================================================
1413

1514
/** Types of nodes in our dependency graph */
16-
export type NodeType = 'component' | 'group' | 'tag' | 'preset' | 'datasource';
15+
export type NodeType = 'component' | 'group' | 'tag' | 'preset';
1716

1817
/** Data that can be stored in a graph node */
19-
export type NodeData = SpaceComponent | SpaceComponentGroup | SpaceComponentInternalTag | SpaceComponentPreset | SpaceDatasource;
18+
export type NodeData = SpaceComponent | SpaceComponentGroup | SpaceComponentInternalTag | SpaceComponentPreset;
2019

2120
/** Target resource information colocated with graph nodes */
2221
export interface TargetResourceInfo<T extends NodeData> {
@@ -57,7 +56,7 @@ export interface SchemaDependencies {
5756
groupUuids: Set<string>;
5857
tagIds: Set<number>;
5958
componentNames: Set<string>;
60-
datasourceNames: Set<string>;
59+
datasourceNames: Set<string>; // Note: Not processed by components push, kept for compatibility
6160
}
6261

6362
// =============================================================================

0 commit comments

Comments
 (0)