Skip to content

Commit

Permalink
Share array-validation logic for JSON and structured headers (#1302)
Browse files Browse the repository at this point in the history
  • Loading branch information
apasel422 committed May 28, 2024
1 parent a01e250 commit 7b86e4f
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 50 deletions.
45 changes: 9 additions & 36 deletions ts/src/header-validator/validate-json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import * as uuid from 'uuid'
import * as constants from '../constants'
import { SourceType } from '../source-type'
import { VendorSpecificValues } from '../vendor-specific-values'
import { Context, PathComponent, ValidationResult } from './context'
import { Context, ValidationResult } from './context'
import { Maybe } from './maybe'
import { CtxFunc } from './validate'
import { CtxFunc, ItemErrorAction, isCollection } from './validate'
import * as validate from './validate'
import * as privacy from '../flexible-event/privacy'

Expand Down Expand Up @@ -411,7 +411,7 @@ function endTimes(
return array(ctx, j, endTime, {
minLength: 1,
maxLength: 5,
keepGoing: false, // suppress unhelpful cascaded errors
itemErrorAction: ItemErrorAction.earlyExit, // suppress unhelpful cascaded errors
})
}

Expand Down Expand Up @@ -448,24 +448,6 @@ function legacyDuration(ctx: Context, j: Json): Maybe<number | bigint> {
})
}

function isCollection<P extends PathComponent, C extends Context = Context>(
ctx: C,
js: Iterable<[P, Json]>,
f: CtxFunc<C, [P, Json], Maybe<unknown>>,
keepGoing: boolean = true
): boolean {
let ok = true
for (const [c, j] of js) {
let itemOk = false
ctx.scope(c, () => f(ctx, [c, j]).peek(() => (itemOk = true)))
if (!itemOk && !keepGoing) {
return false
}
ok = ok && itemOk
}
return ok
}

type SetOpts = ListOpts & {
requireDistinct?: boolean
}
Expand Down Expand Up @@ -500,7 +482,7 @@ function set<T extends number | string, C extends Context = Context>(
}

type ArrayOpts = ListOpts & {
keepGoing?: boolean
itemErrorAction?: ItemErrorAction
}

function array<T, C extends Context = Context>(
Expand All @@ -509,18 +491,9 @@ function array<T, C extends Context = Context>(
f: CtxFunc<C, Json, Maybe<T>>,
opts?: ArrayOpts
): Maybe<T[]> {
const arr: T[] = []

return list(ctx, j, opts)
.filter((js) =>
isCollection(
ctx,
js.entries(),
(ctx, [_i, j]) => f(ctx, j).peek((v) => arr.push(v)),
opts?.keepGoing
)
)
.map(() => arr)
return list(ctx, j, opts).map((js) =>
validate.array(ctx, js.entries(), f, opts?.itemErrorAction)
)
}

function filterDataKeyValue(
Expand Down Expand Up @@ -876,7 +849,7 @@ function summaryBuckets(
minLength: 1,
maxLength,
maxLengthErrSuffix: ' (max_event_level_reports)',
keepGoing: false, // suppress unhelpful cascaded errors
itemErrorAction: ItemErrorAction.earlyExit, // suppress unhelpful cascaded errors
})
}

Expand Down Expand Up @@ -1596,7 +1569,7 @@ function triggerSummaryBucket(ctx: Context, j: Json): Maybe<[number, number]> {
return array(ctx, j, endpoint, {
minLength: 2,
maxLength: 2,
keepGoing: false,
itemErrorAction: ItemErrorAction.earlyExit,
}) as Maybe<[number, number]>
}

Expand Down
24 changes: 11 additions & 13 deletions ts/src/header-validator/validate-os.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Context, ValidationResult } from './context'
import { Maybe } from './maybe'
import * as validate from './validate'
import {
InnerList,
Item,
Expand All @@ -13,18 +14,18 @@ export type OsItem = {
debugReporting: boolean
}

function parseItem(ctx: Context, member: InnerList | Item): OsItem | undefined {
function parseItem(ctx: Context, member: InnerList | Item): Maybe<OsItem> {
if (typeof member[0] !== 'string') {
ctx.warning('ignored, must be a string')
return
return Maybe.None
}

let url
try {
url = new URL(member[0])
} catch {
ctx.warning('ignored, must contain a valid URL')
return
return Maybe.None
}

let debugReporting = false
Expand All @@ -43,7 +44,7 @@ function parseItem(ctx: Context, member: InnerList | Item): OsItem | undefined {
})
}

return { url, debugReporting }
return Maybe.some({ url, debugReporting })
}

export function validateOsRegistration(
Expand All @@ -59,16 +60,13 @@ export function validateOsRegistration(
return [ctx.finish(msg), Maybe.None]
}

const items: OsItem[] = []
list.forEach((member, i) =>
ctx.scope(i, () => {
const item = parseItem(ctx, member)
if (item) {
items.push(item)
}
})
const items = validate.array(
ctx,
list.entries(),
parseItem,
validate.ItemErrorAction.ignore
)
return [ctx.finish(), Maybe.some(items)]
return [ctx.finish(), items]
}

export function serializeOsRegistration(items: OsItem[]): string {
Expand Down
55 changes: 54 additions & 1 deletion ts/src/header-validator/validate.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Context } from './context'
import { Context, PathComponent } from './context'
import { Maybe, Maybeable } from './maybe'

export type CtxFunc<C extends Context, I, O> = (ctx: C, i: I) => O
Expand Down Expand Up @@ -132,3 +132,56 @@ export function make<D, V>(
struct: struct(unknownKeys, warnUnknownMsg),
}
}

export enum ItemErrorAction {
ignore,
reportButKeepGoing,
earlyExit,
}

export function isCollection<
P extends PathComponent,
V,
C extends Context = Context,
>(
ctx: C,
vs: Iterable<[P, V]>,
f: CtxFunc<C, [P, V], Maybe<unknown>>,
itemErrorAction: ItemErrorAction = ItemErrorAction.reportButKeepGoing
): boolean {
let ok = true
for (const [c, v] of vs) {
let itemOk = false
ctx.scope(c, () => f(ctx, [c, v]).peek(() => (itemOk = true)))
if (!itemOk) {
if (itemErrorAction === ItemErrorAction.earlyExit) {
return false
}
if (itemErrorAction === ItemErrorAction.reportButKeepGoing) {
ok = false
}
}
}
return ok
}

export function array<T, V, C extends Context = Context>(
ctx: C,
vs: Iterable<[number, V]>,
f: CtxFunc<C, V, Maybe<T>>,
itemErrorAction: ItemErrorAction = ItemErrorAction.reportButKeepGoing
): Maybe<T[]> {
const arr: T[] = []

if (
!isCollection(
ctx,
vs,
(ctx, [_i, v]) => f(ctx, v).peek((v) => arr.push(v)),
itemErrorAction
)
) {
return Maybe.None
}
return Maybe.some(arr)
}

0 comments on commit 7b86e4f

Please sign in to comment.