Skip to content

Commit

Permalink
fix issue when there's graphql/typescript mismatch with unique key va…
Browse files Browse the repository at this point in the history
…lidation
  • Loading branch information
lolopinto committed Aug 30, 2023
1 parent 9d4d6f5 commit 99eb101
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 13 deletions.
83 changes: 75 additions & 8 deletions ts/src/schema/struct_field.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ describe("struct as list", () => {
});
const f2 = structListTypeF(
{
uuid: UUIDType(),
uuidUnique: UUIDType(),
int: IntegerType(),
string: StringType(),
bool: BooleanType(),
Expand All @@ -314,7 +314,7 @@ describe("struct as list", () => {
enum: EnumType({ values: ["yes", "no", "maybe"] }),
},
{
validateUniqueKey: "uuid",
validateUniqueKey: "uuidUnique",
},
);

Expand Down Expand Up @@ -353,11 +353,11 @@ describe("struct as list", () => {
expect(f.format(data)).toBe(JSON.stringify(format));
});

test("valid unique key ", async () => {
test("valid unique key camel case", async () => {
const d = new Date();
const d2 = new Date();
const val = {
uuid: v1(),
uuidUnique: v1(),
int: 2,
string: "string",
bool: false,
Expand All @@ -366,7 +366,7 @@ describe("struct as list", () => {
enum: "yes",
};
const val2 = {
uuid: v1(),
uuidUnique: v1(),
int: 3,
string: "string",
bool: true,
Expand All @@ -375,24 +375,91 @@ describe("struct as list", () => {
enum: "yes",
};
const formatted1 = {
uuid_unique: val.uuidUnique,
...val,
ts: d.toISOString(),
};
// @ts-expect-error
delete formatted1["uuidUnique"];
const formatted2 = {
uuid_unique: val2.uuidUnique,
...val2,
ts: d2.toISOString(),
};
// @ts-expect-error
delete formatted2["uuidUnique"];

const data = [val, val2];
const format = [formatted1, formatted2];
expect(await f2.valid(data)).toBe(true);
expect(f2.format(data)).toBe(JSON.stringify(format));
});

test("invvalid unique key ", async () => {
test("valid unique key snake case", async () => {
const d = new Date();
const d2 = new Date();
const val = {
uuid: v1(),
uuid_unique: v1(),
int: 2,
string: "string",
bool: false,
ts: d,
float: 1.0,
enum: "yes",
};
const val2 = {
uuid_unique: v1(),
int: 3,
string: "string",
bool: true,
ts: d2,
float: 2.0,
enum: "yes",
};
const formatted1 = {
...val,
ts: d.toISOString(),
};
const formatted2 = {
...val2,
ts: d2.toISOString(),
};
const data = [val, val2];
const format = [formatted1, formatted2];
expect(await f2.valid(data)).toBe(true);
expect(f2.format(data)).toBe(JSON.stringify(format));
});

test("invalid unique key", async () => {
const d = new Date();
const d2 = new Date();
const val = {
uuidUnique: v1(),
int: 2,
string: "string",
bool: false,
ts: d,
float: 1.0,
enum: "yes",
};
const val2 = {
uuidUnique: val.uuidUnique,
int: 3,
string: "string",
bool: true,
ts: d2,
float: 2.0,
enum: "yes",
};
const data = [val, val2];
expect(await f2.valid(data)).toBe(false);
});

test("invalid unique key. storage_case", async () => {
const d = new Date();
const d2 = new Date();
const val = {
uuid_unique: v1(),
int: 2,
string: "string",
bool: false,
Expand All @@ -401,7 +468,7 @@ describe("struct as list", () => {
enum: "yes",
};
const val2 = {
uuid: val.uuid,
uuid_unique: val.uuid_unique,
int: 3,
string: "string",
bool: true,
Expand Down
45 changes: 40 additions & 5 deletions ts/src/schema/struct_field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ export class StructField extends BaseField implements Field {
dbType: DBType.JSONB,
};

private validateUniqueKey?: string | undefined;
private checkUniqueKey: boolean;

constructor(private options: StructOptions, private jsonAsList?: boolean) {
super();
this.type.subFields = options.fields;
Expand All @@ -56,6 +59,10 @@ export class StructField extends BaseField implements Field {
dbType: DBType.JSONB,
};
}
if (options.validateUniqueKey) {
this.validateUniqueKey = options.validateUniqueKey;
this.checkUniqueKey = true;
}
}

formatImpl(obj: any, nested?: boolean) {
Expand Down Expand Up @@ -151,10 +158,31 @@ export class StructField extends BaseField implements Field {
let camelKey = camelCase(k);
let val = obj[camelKey];

let uniqueKeyField = false;
if (
this.validateUniqueKey !== undefined &&
(camelKey === this.validateUniqueKey ||
k === this.validateUniqueKey ||
dbKey === this.validateUniqueKey)
) {
// this.validateUniqueKey = camelKey;
uniqueKeyField = true;
}

if (uniqueKeyField && this.checkUniqueKey) {
this.validateUniqueKey = camelKey;
}

if (val === undefined && obj[dbKey] !== undefined) {
if (uniqueKeyField && this.checkUniqueKey) {
this.validateUniqueKey = dbKey;
}
val = obj[dbKey];
}

// we've processed this once and no need to process this again
if (uniqueKeyField && this.checkUniqueKey) {
this.checkUniqueKey = false;
}
if (val === undefined || val === null) {
// nullable, nothing to do here
if (field.nullable) {
Expand Down Expand Up @@ -212,17 +240,24 @@ export class StructField extends BaseField implements Field {
if (!Array.isArray(obj)) {
return false;
}
// hmm shared instance across tests means this is needed.
// are there other places that have an issue like this???
this.checkUniqueKey = true;
const unique = new Set();
const valid = await Promise.all(
obj.map((v) => {
if (this.options.validateUniqueKey) {
const value = v[this.options.validateUniqueKey];
obj.map(async (v) => {
const valid = await this.validImpl(v);
if (!valid) {
return false;
}
if (this.validateUniqueKey) {
let value = v[this.validateUniqueKey];
if (unique.has(value)) {
return false;
}
unique.add(value);
}
return this.validImpl(v);
return true;
}),
);
return valid.every((b) => b);
Expand Down

0 comments on commit 99eb101

Please sign in to comment.