Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🌊 Streams routing UI #201427

Merged
merged 115 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from 108 commits
Commits
Show all changes
115 commits
Select commit Hold shift + click to select a range
5fe1078
Initial setup for streams plugin
simianhacker Oct 31, 2024
6f940eb
migrating to the streams naming schema; adding an abstraction for the…
simianhacker Nov 1, 2024
9b33808
ensure forked streams are marked root:false
simianhacker Nov 1, 2024
c8cd0f7
Commit entities plugins
dgieselaar Nov 6, 2024
7e8f2d7
Move files into streams_*
dgieselaar Nov 6, 2024
999dfef
Rename stuff & remove stuff
dgieselaar Nov 6, 2024
d99a3ff
stream management
flash1293 Nov 8, 2024
0e89345
add delete and listing endpoint
flash1293 Nov 8, 2024
9dd5fde
cleanup
flash1293 Nov 8, 2024
7400a3b
add resync endpoint
flash1293 Nov 8, 2024
5140687
Remove cruft, handle package relocation
dgieselaar Nov 10, 2024
797c93d
Streams table
dgieselaar Nov 10, 2024
fcce628
Only fetch histogram when DS exists
dgieselaar Nov 10, 2024
9d37219
more fixes
flash1293 Nov 10, 2024
d622fa1
more adjustments
flash1293 Nov 11, 2024
fbc6c2a
some more fixes
flash1293 Nov 11, 2024
45787d0
Merge remote-tracking branch 'upstream/main' into init-streams-plugin
flash1293 Nov 12, 2024
e810f53
[CI] Auto-commit changed files from 'node scripts/build_plugin_list_d…
kibanamachine Nov 12, 2024
e93ac17
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Nov 12, 2024
cf9f349
add limits
flash1293 Nov 12, 2024
0d676ed
Merge branch 'init-streams-plugin' of github.com:simianhacker/kibana …
flash1293 Nov 12, 2024
7b0a4e5
Add streams to left nav
dgieselaar Nov 12, 2024
34f3c44
fix permissions
flash1293 Nov 12, 2024
0b7de24
remove feature completely
flash1293 Nov 12, 2024
76ed81d
cleanup
flash1293 Nov 12, 2024
2427d2e
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Nov 12, 2024
2ad08f4
Merge branch 'main' into init-streams-plugin
flash1293 Nov 12, 2024
c8d99ed
Merge branch 'main' into init-streams-plugin
flash1293 Nov 12, 2024
8c707d1
Merge commit '0d676edfc0681f18d3804e079bad09053bf32247' into init-str…
dgieselaar Nov 12, 2024
e6bb640
[Observability] Split up observability-utils package
dgieselaar Nov 12, 2024
421ea5b
Merge branch 'main' of github.com:elastic/kibana into observability-u…
dgieselaar Nov 12, 2024
7992539
Fix references
dgieselaar Nov 12, 2024
95ae9f8
[CI] Auto-commit changed files from 'node scripts/generate codeowners'
kibanamachine Nov 12, 2024
7a88cc5
Fix issues in APM plugin
dgieselaar Nov 12, 2024
74f0678
Merge branch 'observability-utils-pkg-split' of github.com:dgieselaar…
dgieselaar Nov 12, 2024
dab77c1
[CI] Auto-commit changed files from 'node scripts/yarn_deduplicate'
kibanamachine Nov 12, 2024
a60a165
Updating the owners and CODEOWNERS to @simianhacker @flash1293 @dgies…
simianhacker Nov 12, 2024
fc031f8
Merge branch 'main' of github.com:elastic/kibana into init-streams-pl…
simianhacker Nov 12, 2024
e9189e0
Merge branch 'main' of github.com:elastic/kibana into observability-u…
dgieselaar Nov 13, 2024
08aef83
Merge branch 'observability-utils-pkg-split' of github.com:dgieselaar…
dgieselaar Nov 13, 2024
2bef512
Fix references
dgieselaar Nov 13, 2024
5c10198
Option to unset value on error
dgieselaar Nov 13, 2024
5d40445
Merge branch 'observability-utils-pkg-split' into init-streams-app-pl…
dgieselaar Nov 13, 2024
9ca2da2
Fix lingering import
dgieselaar Nov 13, 2024
8b0b51c
Merge branch 'observability-utils-pkg-split' into init-streams-app-pl…
dgieselaar Nov 13, 2024
d655c0a
Clean up esql signature
dgieselaar Nov 13, 2024
8e0eba8
Handle sync callbacks in withSpan
dgieselaar Nov 13, 2024
eb61e23
Merge branch 'observability-utils-pkg-split' into init-streams-app-pl…
dgieselaar Nov 13, 2024
5e90008
Make APIs internal
dgieselaar Nov 13, 2024
dd55c3a
Merge branch 'init-streams-plugin' into init-streams-app-plugin
dgieselaar Nov 13, 2024
b9896ab
Merge remote-tracking branch 'upstream/main' into init-streams-app-pl…
dgieselaar Nov 13, 2024
377b365
Merge branch 'main' of github.com:elastic/kibana into init-streams-ap…
dgieselaar Nov 13, 2024
b556194
Move to x-pack/plugins/streams_app
dgieselaar Nov 13, 2024
aa4a2e0
Improve types
dgieselaar Nov 13, 2024
f0276dc
Improve type strictness
dgieselaar Nov 13, 2024
adcd660
Update .i18nrc.json
dgieselaar Nov 14, 2024
224ffeb
Merge branch 'main' of github.com:elastic/kibana into init-streams-ap…
dgieselaar Nov 14, 2024
2edc694
Merge branch 'init-streams-app-plugin' of github.com:dgieselaar/kiban…
dgieselaar Nov 14, 2024
bb4dc6e
Conditionally register items in side nav
dgieselaar Nov 14, 2024
2b74f6c
Clean up
dgieselaar Nov 14, 2024
46ffca7
[CI] Auto-commit changed files from 'node scripts/build_plugin_list_d…
kibanamachine Nov 14, 2024
6e87efd
place nice with ES
flash1293 Nov 14, 2024
d0c05f3
[CI] Auto-commit changed files from 'make api-docs'
kibanamachine Nov 14, 2024
f663b43
Merge branch 'main' into flash1293/streams-improvements
flash1293 Nov 15, 2024
6373996
Merge branch 'main' into flash1293/streams-improvements
flash1293 Nov 19, 2024
18ca51e
streams data generation
flash1293 Nov 19, 2024
a8eefbe
leave comment
flash1293 Nov 19, 2024
ef8a4c1
fix
flash1293 Nov 20, 2024
be2712b
Merge remote-tracking branch 'upstream/main' into flash1293/streams-d…
flash1293 Nov 20, 2024
7fd7cb8
add missing things
flash1293 Nov 20, 2024
105ef6e
Merge branch 'init-streams-app-plugin' into flash1293/streams-routing-ui
flash1293 Nov 20, 2024
3fdc9cb
wip
flash1293 Nov 21, 2024
a1279c7
Merge branch 'flash1293/streams-improvements' into flash1293/streams-…
flash1293 Nov 21, 2024
9c0dfbd
Merge branch 'main' into flash1293/streams-improvements
flash1293 Nov 21, 2024
9865d73
add more stuff
flash1293 Nov 22, 2024
e71a5a9
Merge branch 'main' into flash1293/streams-improvements
flash1293 Nov 22, 2024
d1066c2
Merge branch 'main' into flash1293/streams-capabilities
flash1293 Nov 22, 2024
64bb157
add more stuff
flash1293 Nov 22, 2024
4741073
some smaller fixes
flash1293 Nov 22, 2024
d7ac8cd
make it pretty
flash1293 Nov 22, 2024
609f66e
better condition editor
flash1293 Nov 22, 2024
43b9aaa
Merge branch 'main' into flash1293/streams-improvements
flash1293 Nov 25, 2024
ef1c23f
Merge remote-tracking branch 'upstream/main' into flash1293/streams-i…
flash1293 Nov 25, 2024
90e7708
Merge branch 'flash1293/streams-improvements' of github.com:flash1293…
flash1293 Nov 25, 2024
4ad40a6
Merge remote-tracking branch 'upstream/main' into flash1293/streams-c…
flash1293 Nov 25, 2024
f00e883
Merge remote-tracking branch 'upstream/main' into flash1293/streams-r…
flash1293 Nov 25, 2024
b78a139
fix some stuff
flash1293 Nov 25, 2024
5d8ffbf
Merge remote-tracking branch 'upstream/main' into flash1293/streams-i…
flash1293 Nov 25, 2024
a55fe17
fix
flash1293 Nov 25, 2024
3bd3a9a
fix
flash1293 Nov 25, 2024
d92d0e8
Merge branch 'flash1293/streams-capabilities' into flash1293/streams-…
flash1293 Nov 25, 2024
20568a6
Merge branch 'flash1293/streams-improvements' into flash1293/streams-…
flash1293 Nov 25, 2024
7e2fc11
remove unintended changes
flash1293 Nov 25, 2024
72eb0cc
management base page
flash1293 Nov 25, 2024
417f68f
Merge branch 'flash1293/streams-management-ui' into flash1293/streams…
flash1293 Nov 25, 2024
b34dc28
fix link
flash1293 Nov 25, 2024
be3695f
Merge branch 'main' into flash1293/streams-routing-ui
flash1293 Nov 26, 2024
28e5d14
make layout work
flash1293 Nov 27, 2024
510aa65
add delete modal
flash1293 Nov 27, 2024
23eeda9
add move up button
flash1293 Nov 27, 2024
3ec9857
cleanuops
flash1293 Nov 27, 2024
8d91298
more cleanups
flash1293 Nov 27, 2024
8ad507b
Merge remote-tracking branch 'upstream/main' into flash1293/streams-r…
flash1293 Nov 27, 2024
0b843c2
fix page height in different nav modes
flash1293 Nov 27, 2024
909e62b
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Nov 27, 2024
91d0921
Merge remote-tracking branch 'upstream/main' into flash1293/streams-r…
flash1293 Nov 28, 2024
29b1a21
fix types
flash1293 Nov 28, 2024
cec06fa
Merge branch 'flash1293/streams-routing-ui' of github.com:flash1293/k…
flash1293 Nov 28, 2024
f01e4f2
Merge remote-tracking branch 'upstream/main' into flash1293/streams-r…
flash1293 Nov 29, 2024
22baed7
fix
flash1293 Nov 29, 2024
a6730af
use debounce
flash1293 Nov 29, 2024
09f30a2
Merge branch 'main' into flash1293/streams-routing-ui
flash1293 Dec 2, 2024
e4a89cf
Merge remote-tracking branch 'upstream/main' into flash1293/streams-r…
flash1293 Dec 3, 2024
a49933e
Merge branch 'flash1293/streams-routing-ui' of github.com:flash1293/k…
flash1293 Dec 3, 2024
9f5b0f6
add comment
flash1293 Dec 3, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -787,12 +787,17 @@ export const QueryBarTopRow = React.memo(
adHocDataview={props.indexPatterns?.[0]}
/>
)}
<EuiFlexItem
grow={!shouldShowDatePickerAsBadge()}
style={{ minWidth: shouldShowDatePickerAsBadge() ? 'auto' : 320, maxWidth: '100%' }}
>
{!isQueryLangSelected ? renderQueryInput() : null}
</EuiFlexItem>
{!isQueryLangSelected && shouldRenderQueryInput() && (
<EuiFlexItem
grow={!shouldShowDatePickerAsBadge()}
style={{
minWidth: shouldShowDatePickerAsBadge() ? 'auto' : 320,
maxWidth: '100%',
}}
>
{renderQueryInput()}
</EuiFlexItem>
)}
{props.renderQueryInputAppend?.()}
{shouldShowDatePickerAsBadge() && props.filterBar}
{renderUpdateButton()}
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/streams/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
* 2.0.
*/

export type { StreamDefinition } from './types';
export type { StreamDefinition, ReadStreamDefinition } from './types';
26 changes: 16 additions & 10 deletions x-pack/plugins/streams/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ export interface AndCondition {
and: Condition[];
}

export interface RerouteOrCondition {
export interface OrCondition {
or: Condition[];
}

export type Condition = FilterCondition | AndCondition | RerouteOrCondition | undefined;
export type Condition = FilterCondition | AndCondition | OrCondition | undefined;

export const conditionSchema: z.ZodType<Condition> = z.lazy(() =>
z.union([
Expand Down Expand Up @@ -77,17 +77,17 @@ export const fieldDefinitionSchema = z.object({

export type FieldDefinition = z.infer<typeof fieldDefinitionSchema>;

export const streamChildSchema = z.object({
id: z.string(),
condition: z.optional(conditionSchema),
});

export type StreamChild = z.infer<typeof streamChildSchema>;

export const streamWithoutIdDefinitonSchema = z.object({
processing: z.array(processingDefinitionSchema).default([]),
fields: z.array(fieldDefinitionSchema).default([]),
children: z
.array(
z.object({
id: z.string(),
condition: z.optional(conditionSchema),
})
)
.default([]),
children: z.array(streamChildSchema).default([]),
});

export type StreamWithoutIdDefinition = z.infer<typeof streamDefinitonSchema>;
Expand All @@ -101,3 +101,9 @@ export type StreamDefinition = z.infer<typeof streamDefinitonSchema>;
export const streamDefinitonWithoutChildrenSchema = streamDefinitonSchema.omit({ children: true });

export type StreamWithoutChildrenDefinition = z.infer<typeof streamDefinitonWithoutChildrenSchema>;

export const readStreamDefinitonSchema = streamDefinitonSchema.extend({
inheritedFields: z.array(fieldDefinitionSchema.extend({ from: z.string() })).default([]),
});

export type ReadStreamDefinition = z.infer<typeof readStreamDefinitonSchema>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { Condition, FilterCondition } from '../../../../common/types';
import { isAndCondition, isFilterCondition, isOrCondition } from './condition_guards';

export function isComplete(condition: Condition): boolean {
if (isFilterCondition(condition)) {
return condition.field !== undefined && condition.field !== '';
}
if (isAndCondition(condition)) {
return condition.and.every(isComplete);
}
if (isOrCondition(condition)) {
return condition.or.every(isComplete);
}
return false;
}

export function getFields(
condition: Condition
): Array<{ name: string; type: 'number' | 'string' }> {
const fields = collectFields(condition);
// deduplicate fields, if mapped as string and number, keep as number
const uniqueFields = new Map<string, 'number' | 'string'>();
fields.forEach((field) => {
const existing = uniqueFields.get(field.name);
if (existing === 'number') {
return;
}
if (existing === 'string' && field.type === 'number') {
uniqueFields.set(field.name, 'number');
return;
}
uniqueFields.set(field.name, field.type);
});

return Array.from(uniqueFields).map(([name, type]) => ({ name, type }));
}

function collectFields(condition: Condition): Array<{ name: string; type: 'number' | 'string' }> {
if (isFilterCondition(condition)) {
return [{ name: condition.field, type: getFieldTypeForFilterCondition(condition) }];
}
if (isAndCondition(condition)) {
return condition.and.flatMap(collectFields);
}
if (isOrCondition(condition)) {
return condition.or.flatMap(collectFields);
}
return [];
}

function getFieldTypeForFilterCondition(condition: FilterCondition): 'number' | 'string' {
switch (condition.operator) {
case 'gt':
case 'gte':
case 'lt':
case 'lte':
return 'number';
case 'neq':
case 'eq':
case 'exists':
case 'contains':
case 'startsWith':
case 'endsWith':
case 'notExists':
return 'string';
default:
return 'string';
}
}

export function validateCondition(condition: Condition) {
if (isFilterCondition(condition)) {
// check whether a field is specified
if (!condition.field.trim()) {
throw new Error('Field is required in conditions');
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import {
AndCondition,
conditionSchema,
FilterCondition,
filterConditionSchema,
OrCondition,
} from '../../../../common/types';

export function isFilterCondition(subject: any): subject is FilterCondition {
const result = filterConditionSchema.safeParse(subject);
return result.success;
}

export function isAndCondition(subject: any): subject is AndCondition {
const result = conditionSchema.safeParse(subject);
return result.success && subject.and != null;
}

export function isOrCondition(subject: any): subject is OrCondition {
const result = conditionSchema.safeParse(subject);
return result.success && subject.or != null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,12 @@

import { isBoolean, isString } from 'lodash';
import {
AndCondition,
BinaryFilterCondition,
Condition,
conditionSchema,
FilterCondition,
filterConditionSchema,
RerouteOrCondition,
UnaryFilterCondition,
} from '../../../../common/types';

function isFilterCondition(subject: any): subject is FilterCondition {
const result = filterConditionSchema.safeParse(subject);
return result.success;
}

function isAndCondition(subject: any): subject is AndCondition {
const result = conditionSchema.safeParse(subject);
return result.success && subject.and != null;
}

function isOrCondition(subject: any): subject is RerouteOrCondition {
const result = conditionSchema.safeParse(subject);
return result.success && subject.or != null;
}
import { isAndCondition, isFilterCondition, isOrCondition } from './condition_guards';

function safePainlessField(condition: FilterCondition) {
return `ctx.${condition.field.split('.').join('?.')}`;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { Condition, FilterCondition } from '../../../../common/types';
import { isAndCondition, isFilterCondition, isOrCondition } from './condition_guards';

function conditionToClause(condition: FilterCondition) {
switch (condition.operator) {
case 'neq':
return { bool: { must_not: { match: { [condition.field]: condition.value } } } };
case 'eq':
return { match: { [condition.field]: condition.value } };
case 'exists':
return { exists: { field: condition.field } };
case 'gt':
return { range: { [condition.field]: { gt: condition.value } } };
case 'gte':
return { range: { [condition.field]: { gte: condition.value } } };
case 'lt':
return { range: { [condition.field]: { lt: condition.value } } };
case 'lte':
return { range: { [condition.field]: { lte: condition.value } } };
case 'contains':
return { wildcard: { [condition.field]: `*${condition.value}*` } };
case 'startsWith':
return { prefix: { [condition.field]: condition.value } };
case 'endsWith':
return { wildcard: { [condition.field]: `*${condition.value}` } };
case 'notExists':
return { bool: { must_not: { exists: { field: condition.field } } } };
default:
return { match_none: {} };
}
}

export function conditionToQueryDsl(condition: Condition): any {
if (isFilterCondition(condition)) {
return conditionToClause(condition);
}
if (isAndCondition(condition)) {
const and = condition.and.map((filter) => conditionToQueryDsl(filter));
return {
bool: {
must: and,
},
};
}
if (isOrCondition(condition)) {
const or = condition.or.map((filter) => conditionToQueryDsl(filter));
return {
bool: {
should: or,
},
};
}
return {
match_none: {},
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,9 @@ export const logsDefaultPipelineProcessors = [
ignore_missing_pipeline: true,
},
},
{
dot_expander: {
field: '*',
},
},
];
13 changes: 8 additions & 5 deletions x-pack/plugins/streams/server/lib/streams/stream_crud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,12 @@ export async function listStreams({
sort: [{ id: 'asc' }],
});
const definitions = response.hits.hits.map((hit) => hit._source!);
const total = response.hits.total!;

const hasAccess = await Promise.all(
definitions.map((definition) => checkReadAccess({ id: definition.id, scopedClusterClient }))
);
return {
definitions,
total: typeof total === 'number' ? total : total.value,
definitions: definitions.filter((_, index) => hasAccess[index]),
total: definitions.length,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should total reflect the overall total or the total the user hasAccess to?

If the former (as implemented here) is there any risk avenue in "leaking" that there are potentially more entries?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point - there isn't really a point of sending total at the moment as we don't have pagination anyway. I just removed it

};
}

Expand Down Expand Up @@ -163,7 +164,9 @@ export async function readAncestors({

return {
ancestors: await Promise.all(
ancestorIds.map((ancestorId) => readStream({ scopedClusterClient, id: ancestorId }))
ancestorIds.map((ancestorId) =>
readStream({ scopedClusterClient, id: ancestorId, skipAccessCheck: true })
)
),
};
}
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/streams/server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { forkStreamsRoute } from './streams/fork';
import { listStreamsRoute } from './streams/list';
import { readStreamRoute } from './streams/read';
import { resyncStreamsRoute } from './streams/resync';
import { sampleStreamRoute } from './streams/sample';
import { streamsStatusRoutes } from './streams/settings';

export const streamsRouteRepository = {
Expand All @@ -27,6 +28,7 @@ export const streamsRouteRepository = {
...streamsStatusRoutes,
...esqlRoutes,
...disableStreamsRoute,
...sampleStreamRoute,
};

export type StreamsRouteRepository = typeof streamsRouteRepository;
10 changes: 7 additions & 3 deletions x-pack/plugins/streams/server/routes/streams/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
import { MalformedStreamId } from '../../lib/streams/errors/malformed_stream_id';
import { getParentId } from '../../lib/streams/helpers/hierarchy';
import { MalformedChildren } from '../../lib/streams/errors/malformed_children';
import { validateCondition } from '../../lib/streams/helpers/condition_fields';

export const editStreamRoute = createServerRoute({
endpoint: 'PUT /api/streams/{id}',
Expand Down Expand Up @@ -57,7 +58,7 @@ export const editStreamRoute = createServerRoute({
const parentId = getParentId(params.path.id);
let parentDefinition: StreamDefinition | undefined;

const streamDefinition = { ...params.body };
const streamDefinition = { ...params.body, id: params.path.id };

// always need to go from the leaves to the parent when syncing ingest pipelines, otherwise data
// will be routed before the data stream is ready
Expand Down Expand Up @@ -87,7 +88,7 @@ export const editStreamRoute = createServerRoute({

await syncStream({
scopedClusterClient,
definition: { ...streamDefinition, id: params.path.id },
definition: streamDefinition,
rootDefinition: parentDefinition,
logger,
});
Expand Down Expand Up @@ -150,7 +151,7 @@ async function updateParentStream(
async function validateStreamChildren(
scopedClusterClient: IScopedClusterClient,
id: string,
children: Array<{ id: string }>
children: StreamDefinition['children']
) {
try {
const { definition: oldDefinition } = await readStream({
Expand All @@ -159,6 +160,9 @@ async function validateStreamChildren(
});
const oldChildren = oldDefinition.children.map((child) => child.id);
const newChildren = new Set(children.map((child) => child.id));
children.forEach((child) => {
validateCondition(child.condition);
});
if (oldChildren.some((child) => !newChildren.has(child))) {
throw new MalformedChildren(
'Cannot remove children from a stream, please delete the stream instead'
Expand Down
Loading