Skip to content
This repository has been archived by the owner on Apr 2, 2023. It is now read-only.

Commit

Permalink
feat: add optional maxDepth option to recursive functions: containsPI…
Browse files Browse the repository at this point in the history
…I, detect, redact, unwrapObject
  • Loading branch information
tdreyno committed Jun 20, 2021
1 parent 9dd6f37 commit d8437a4
Show file tree
Hide file tree
Showing 32 changed files with 329 additions and 221 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ module.exports = {
"@typescript-eslint/no-unsafe-return": 0,
"@typescript-eslint/no-unsafe-call": 0,
"@typescript-eslint/no-unsafe-member-access": 0,
"@typescript-eslint/no-explicit-any": 0,
},
}
9 changes: 9 additions & 0 deletions src/PIITry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import PII from "./pii"

export default async function PIITry<S>(fn: () => S | Promise<S>): Promise<S> {
try {
return fn()
} catch (e) {
throw PII(e, "REDACTED_ERROR")
}
}
3 changes: 2 additions & 1 deletion src/__tests__/constructor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { PII, unwrap } from "../index"
import PII from "../pii"
import unwrap from "../unwrap"

describe("PII", () => {
it("should not leak into toString", () => {
Expand Down
7 changes: 6 additions & 1 deletion src/__tests__/containsPII.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { PII, containsPII } from "../index"
import PII from "../pii"
import containsPII from "../containsPII"

describe("containsPII", () => {
it("should not find PII", () => {
Expand All @@ -20,4 +21,8 @@ describe("containsPII", () => {
expect(containsPII([PII("test")])).toBeTruthy()
expect(containsPII({ test: PII(1) })).toBeTruthy()
})

it("should return true after max depth", () => {
expect(containsPII({ test: [{ hello: "world" }] }, 2)).toBeTruthy()
})
})
11 changes: 9 additions & 2 deletions src/__tests__/detect.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { detect, isPII } from "../index"
import { isPII } from "../pii"
import detect from "../detect"
import unwrap from "../unwrap"

const detector = (data: unknown) => Array.isArray(data)

Expand All @@ -25,4 +26,10 @@ describe("detect", () => {
const detectedArrays = detect(detector, { test: set })
expect(isPII(Array.from((detectedArrays as any).test)[0])).toBeTruthy()
})

it("should return PII after max depth", () => {
const result: any = detect(() => false, { test: [{ hello: "world" }] }, 2)

expect(unwrap(result.test[0])).toEqual({ hello: "world" })
})
})
4 changes: 3 additions & 1 deletion src/__tests__/fold.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { PII, fold, unwrap } from "../index"
import PII from "../pii"
import fold from "../fold"
import unwrap from "../unwrap"

describe("fold", () => {
it("should fold multiple PII", () => {
Expand Down
4 changes: 3 additions & 1 deletion src/__tests__/map.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { PII, map, unwrap } from "../index"
import PII from "../pii"
import map from "../map"
import unwrap from "../unwrap"

describe("map", () => {
it("should map inside PII", () => {
Expand Down
9 changes: 8 additions & 1 deletion src/__tests__/redact.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { PII, redact } from "../index"
import PII from "../pii"
import redact from "../redact"

const REDACTED = "REDACTED"
const redactor = () => REDACTED
Expand Down Expand Up @@ -62,4 +63,10 @@ describe("redact", () => {
two: REDACTED,
})
})

it("should return PII after max depth", () => {
expect(redact(redactor, { test: [{ hello: "world" }] }, 2)).toEqual({
test: [REDACTED],
})
})
})
4 changes: 3 additions & 1 deletion src/__tests__/tap.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { PII, tap, unwrap } from "../index"
import PII from "../pii"
import tap from "../tap"
import unwrap from "../unwrap"

describe("tap", () => {
it("should tap inside PII", () => {
Expand Down
3 changes: 2 additions & 1 deletion src/__tests__/test.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { PII, test } from "../index"
import PII from "../pii"
import test from "../test"

describe("test", () => {
it("should test predicate against PII", () => {
Expand Down
3 changes: 2 additions & 1 deletion src/__tests__/try.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { PIITry, unwrap } from "../index"
import PIITry from "../PIITry"
import unwrap from "../unwrap"

describe("PIITry", () => {
it("should wrap results in PII", async () => {
Expand Down
3 changes: 2 additions & 1 deletion src/__tests__/unwrap.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { PII, unwrap } from "../index"
import PII from "../pii"
import unwrap from "../unwrap"

describe("unwrap", () => {
it("upwraps a value", () => {
Expand Down
9 changes: 8 additions & 1 deletion src/__tests__/unwrapObject.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { PII, unwrapObject } from "../index"
import PII from "../pii"
import unwrapObject from "../unwrapObject"

describe("unwrapObject", () => {
it("should remove all wrappers", () => {
Expand Down Expand Up @@ -59,4 +60,10 @@ describe("unwrapObject", () => {
two: 2,
})
})

it("should return null after max depth", () => {
expect(unwrapObject({ test: [{ hello: "world" }] }, 2)).toEqual({
test: [null],
})
})
})
6 changes: 5 additions & 1 deletion src/__tests__/zipWith.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { PII, zip2With, zip3With, zip4With, unwrap } from "../index"
import PII from "../pii"
import zip2With from "../zip2With"
import zip3With from "../zip3With"
import zip4With from "../zip4With"
import unwrap from "../unwrap"

describe("zipWith", () => {
it("should zipWith two different types of PII", () => {
Expand Down
20 changes: 20 additions & 0 deletions src/containsPII.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { isPII } from "./pii"
import visit from "./visit"

const containsPII = (input: unknown, maxDepth = Infinity): boolean =>
maxDepth === 0 || isPII(input)
? true
: visit(input, {
record: o => Object.values(o).some(i => containsPII(i, maxDepth - 1)),
map: m =>
Array.from(m).some(
([k, v]) =>
containsPII(k, maxDepth - 1) || containsPII(v, maxDepth - 1),
),
array: a => a.some(i => containsPII(i, maxDepth - 1)),
set: s => Array.from(s).some(i => containsPII(i, maxDepth - 1)),
primitive: p => isPII(p),
object: p => isPII(p),
})

export default containsPII
33 changes: 33 additions & 0 deletions src/detect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import PII, { isPII } from "./pii"
import visit from "./visit"

const detect = (
detector: (data: unknown) => boolean,
input: unknown,
maxDepth = Infinity,
): unknown =>
isPII(input)
? input
: detector(input) || maxDepth === 0
? PII(input)
: visit(input, {
record: r =>
Object.keys(r).reduce((sum, key) => {
sum[key] = detect(detector, r[key], maxDepth - 1)
return sum
}, {} as Record<string, unknown>),
map: m =>
new Map(
Array.from(m).map(([k, v]) => [
detect(detector, k, maxDepth - 1),
detect(detector, v, maxDepth - 1),
]),
),
array: a => a.map(x => detect(detector, x, maxDepth - 1)),
set: s =>
new Set(Array.from(s).map(x => detect(detector, x, maxDepth - 1))),
primitive: p => p,
object: o => o,
})

export default detect
13 changes: 13 additions & 0 deletions src/fold.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import PII from "./pii"
import unwrap from "./unwrap"

export default <A, B>(
fn: (
previousValue: B,
currentValue: A,
currentIndex: number,
array: A[],
) => B,
initial: B,
a: Array<PII<A> | A>,
): PII<B> => PII(a.map<A>(unwrap).reduce(fn, initial))
16 changes: 14 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,14 @@
export * from "./pii"
export * from "./result"
export { default as containsPII } from "./containsPII"
export { default as detect } from "./detect"
export { default as fold } from "./fold"
export { default as map } from "./map"
export { default as PII, isPII } from "./pii"
export { default as PIITry } from "./PIITry"
export { default as redact } from "./redact"
export { default as tap } from "./tap"
export { default as test } from "./test"
export { default as unwrap } from "./unwrap"
export { default as unwrapObject } from "./unwrapObject"
export { default as zip2With } from "./zip2With"
export { default as zip3With } from "./zip3With"
export { default as zip4With } from "./zip4With"
5 changes: 5 additions & 0 deletions src/isObject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Function, regex, object, Number, String, etc
export default (value: unknown): boolean => {
const type = typeof value
return value != null && (type == "object" || type == "function")
}
5 changes: 5 additions & 0 deletions src/isRecord.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const proto = Object.prototype
const gpo = Object.getPrototypeOf

export default (obj: unknown): obj is Record<string, unknown> =>
obj === null || typeof obj !== "object" ? false : gpo(obj) === proto
13 changes: 13 additions & 0 deletions src/map.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import PII from "./pii"
import unwrap from "./unwrap"

function map<T, T2>(fn: (item: T) => T2, item: PII<T>): PII<T2>
function map<T, T2>(fn: (item: T) => T2, item: T): Exclude<T2, PII<any>>
function map<T, T2>(
fn: (item: T) => T2,
item: PII<T> | T,
): PII<T2> | Exclude<T2, PII<any>> {
return PII(fn(unwrap(item)))
}

export default map
Loading

0 comments on commit d8437a4

Please sign in to comment.