Fix/empty properties (#1764)

* refactor properties utilities with updated ts-matches and allow for a null file

* update pacakges
This commit is contained in:
Lucy C
2022-08-29 11:58:49 -04:00
committed by GitHub
parent b2d7f4f606
commit 8cd2fac9b9
3 changed files with 2641 additions and 2697 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -52,7 +52,7 @@
"pbkdf2": "^3.1.2", "pbkdf2": "^3.1.2",
"rxjs": "^7.5.6", "rxjs": "^7.5.6",
"swiper": "^8.2.4", "swiper": "^8.2.4",
"ts-matches": "^5.1.7", "ts-matches": "^5.2.0",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"uuid": "^8.3.2", "uuid": "^8.3.2",
"zone.js": "^0.11.5" "zone.js": "^0.11.5"

View File

@@ -1,51 +1,59 @@
import { applyOperation } from 'fast-json-patch' import { applyOperation } from 'fast-json-patch'
import { import matches, {
Parser, Parser,
shape, shape,
string, string,
literal, literal,
boolean, boolean,
object,
deferred, deferred,
dictionary, dictionary,
anyOf, anyOf,
number,
arrayOf,
} from 'ts-matches' } from 'ts-matches'
export type ValidVersion = 1 | 2 type ValidVersion = 1 | 2
function has<Obj extends {}, K extends string>( type PropertiesV1 = typeof matchPropertiesV1._TYPE
obj: Obj, type PackagePropertiesV1 = PropertiesV1[]
key: K, type PackagePropertiesV2 = {
): obj is Obj & { [P in K]: unknown } { [name: string]: PackagePropertyString | PackagePropertyObject
return key in obj
} }
type PackagePropertiesVersionedData<T extends number> = T extends 1
? PackagePropertiesV1
: T extends 2
? PackagePropertiesV2
: never
type PackagePropertyString = typeof matchPackagePropertyString._TYPE
export type PackagePropertiesVersioned<T extends number> = {
version: T
data: PackagePropertiesVersionedData<T>
}
export type PackageProperties = PackagePropertiesV2
const matchPropertiesV1 = shape( const matchPropertiesV1 = shape(
{ {
name: string, name: string,
value: string, value: string,
description: string.optional(), description: string,
copyable: boolean.optional(), copyable: boolean,
qr: boolean.optional(), qr: boolean,
}, },
['description', 'copyable', 'qr'], ['description', 'copyable', 'qr'],
{ copyable: false, qr: false } as const, { copyable: false, qr: false } as const,
) )
type PropertiesV1 = typeof matchPropertiesV1._TYPE
type PackagePropertiesV2 = {
[name: string]: PackagePropertyString | PackagePropertyObject
}
const [matchPackagePropertiesV2, setPPV2] = deferred<PackagePropertiesV2>() const [matchPackagePropertiesV2, setPPV2] = deferred<PackagePropertiesV2>()
const matchPackagePropertyString = shape( const matchPackagePropertyString = shape(
{ {
type: literal('string'), type: literal('string'),
description: string.optional(), description: string,
value: string, value: string,
copyable: boolean.optional(), copyable: boolean,
qr: boolean.optional(), qr: boolean,
masked: boolean.optional(), masked: boolean,
}, },
['description', 'copyable', 'qr', 'masked'], ['description', 'copyable', 'qr', 'masked'],
{ {
@@ -54,16 +62,15 @@ const matchPackagePropertyString = shape(
masked: false, masked: false,
} as const, } as const,
) )
type PackagePropertyString = typeof matchPackagePropertyString._TYPE
const matchPackagePropertyObject = shape( const matchPackagePropertyObject = shape(
{ {
type: literal('object'), type: literal('object'),
value: matchPackagePropertiesV2, value: matchPackagePropertiesV2,
description: string.optional(), description: string,
}, },
['description'], ['description'],
{ description: null as null },
) )
const matchPropertyV2 = anyOf( const matchPropertyV2 = anyOf(
matchPackagePropertyString, matchPackagePropertyString,
matchPackagePropertyObject, matchPackagePropertyObject,
@@ -71,58 +78,29 @@ const matchPropertyV2 = anyOf(
type PackagePropertyObject = typeof matchPackagePropertyObject._TYPE type PackagePropertyObject = typeof matchPackagePropertyObject._TYPE
setPPV2(dictionary([string, matchPropertyV2])) setPPV2(dictionary([string, matchPropertyV2]))
const matchPackagePropertiesVersionedV1 = shape({
version: number,
data: arrayOf(matchPropertiesV1),
})
const matchPackagePropertiesVersionedV2 = shape({
version: number,
data: dictionary([string, matchPropertyV2]),
})
export function parsePropertiesPermissive( export function parsePropertiesPermissive(
properties: unknown, properties: unknown,
errorCallback: (err: Error) => any = console.warn, errorCallback: (err: Error) => any = console.warn,
): PackageProperties { ): PackageProperties {
if (typeof properties !== 'object' || properties === null) { return matches(properties)
errorCallback(new TypeError(`${properties} is not an object`)) .when(matchPackagePropertiesVersionedV1, prop =>
return {} parsePropertiesV1Permissive(prop.data, errorCallback),
} )
// @TODO still need this conditional? .when(matchPackagePropertiesVersionedV2, prop => prop.data)
if ( .when(matches.nill, {})
!has(properties, 'version') || .defaultToLazy(() => {
!has(properties, 'data') || errorCallback(new TypeError(`value is not valid`))
typeof properties.version !== 'number' || return {}
!properties.data })
) {
return Object.entries(properties)
.filter(([_, value]) => {
if (typeof value === 'string') {
return true
} else {
errorCallback(new TypeError(`${value} is not a string`))
return false
}
})
.map(([name, value]) => ({
name,
value: {
value: String(value),
copyable: false,
qr: false,
masked: false,
},
}))
.reduce((acc, { name, value }) => {
// TODO: Fix type
acc[name] = value as any
return acc
}, {} as PackageProperties)
}
switch (properties.version) {
case 1:
return parsePropertiesV1Permissive(properties.data, errorCallback)
case 2:
return parsePropertiesV2Permissive(properties.data, errorCallback)
default:
errorCallback(
new Error(
`unknown properties version ${properties.version}, attempting to parse as v2`,
),
)
return parsePropertiesV2Permissive(properties.data, errorCallback)
}
} }
function parsePropertiesV1Permissive( function parsePropertiesV1Permissive(
@@ -164,37 +142,6 @@ function parsePropertiesV1Permissive(
{}, {},
) )
} }
function parsePropertiesV2Permissive(
properties: unknown,
errorCallback: (err: Error) => any,
): PackageProperties {
if (!object.test(properties)) {
return {}
}
return Object.entries(properties).reduce(
(prev: PackageProperties, [name, value], idx) => {
const result = matchPropertyV2.enumParsed(value)
if ('value' in result) {
prev[name] = result.value
} else {
const error = result.error
const message = Parser.validatorErrorAsString(error)
const dataPath = error.keys.map(removeQuotes).join('/')
errorCallback(new Error(`/data/${idx}: ${message}`))
if (dataPath) {
applyOperation(properties, {
op: 'replace',
path: `/${dataPath}`,
value: undefined,
})
}
}
return prev
},
{},
)
}
const removeRegex = /('|")/ const removeRegex = /('|")/
function removeQuotes(x: string) { function removeQuotes(x: string) {
@@ -203,17 +150,3 @@ function removeQuotes(x: string) {
} }
return x return x
} }
type PackagePropertiesV1 = PropertiesV1[]
export type PackageProperties = PackagePropertiesV2
export type PackagePropertiesVersioned<T extends number> = {
version: T
data: PackagePropertiesVersionedData<T>
}
export type PackagePropertiesVersionedData<T extends number> = T extends 1
? PackagePropertiesV1
: T extends 2
? PackagePropertiesV2
: never