Skip to content

Commit

Permalink
Update the GadgetRecord base type to be strict-er -- records can't wr…
Browse files Browse the repository at this point in the history
…ap null or undefined, they have to wrap an object

This required changing our Select type to always select from a truthy schema (which seems reasonable), but other than that, worked just fine!
  • Loading branch information
airhorns committed Jun 28, 2023
1 parent a071ca8 commit 50f276e
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 6 deletions.
13 changes: 13 additions & 0 deletions packages/api-client-core/spec/GadgetRecord.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,19 @@ describe("GadgetRecord", () => {
};
});

it("can be constructed with base data", () => {
const product = new GadgetRecord<SampleBaseRecord>(productBaseRecord);
expect(product).toBeTruthy();
});

it("can be constructed with null or undefined to create a new instance", () => {
let product = new GadgetRecord<SampleBaseRecord>(null);
expect(product).toBeTruthy();

product = new GadgetRecord<SampleBaseRecord>();
expect(product).toBeTruthy();
});

it("should respond toJSON, which returns the inner __gadget.fields properties", () => {
const product = new GadgetRecord<SampleBaseRecord>(productBaseRecord);
expect(product.toJSON()).toEqual({
Expand Down
49 changes: 49 additions & 0 deletions packages/api-client-core/spec/Select-type.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { AssertTrue, IsExact } from "conditional-type-checks";
import type { Select } from "../src/types";
import type { TestSchema } from "./TestSchema";

describe("Select<>", () => {
type _SelectingProperties = AssertTrue<IsExact<Select<TestSchema, { num: true }>, { num: number }>>;

type _ConditionallySelectingProperties = AssertTrue<
IsExact<Select<TestSchema, { num: true; str: undefined; obj: false }>, { num: number }>
>;

type _SelectingNestedProperties = AssertTrue<
IsExact<
Select<TestSchema, { num: true; obj: { test: true; bool: false; deep: { property: true } } }>,
{ num: number; obj: { test: "test"; deep: { property: string } } }
>
>;

type _optionalNestedPropertySelection = Select<TestSchema, { optionalObj: { test: true } }>;
type _TestSelectingOptionalNestedProperties = AssertTrue<
IsExact<_optionalNestedPropertySelection, { optionalObj: { test: "test" } | null }>
>;

type _listSelection = Select<TestSchema, { list: { stuff: true } }>;
type _TestSelectingLists = AssertTrue<IsExact<_listSelection, { list: { stuff: number[] | null }[] }>>;

type _optionalListSelection = Select<TestSchema, { optionalList: { stuff: true; title: true } }>;
type _TestSelectingOptionalLists = AssertTrue<
IsExact<_optionalListSelection, { optionalList: { title: "listy"; stuff: number[] | null }[] | null }>
>;

type _connectionSelection = Select<
TestSchema,
{ someConnection: { pageInfo: { hasNextPage: true }; edges: { node: { id: true; state: true } } } }
>;
type _TestSelectingConnection = AssertTrue<
IsExact<
_connectionSelection,
{
someConnection: {
pageInfo: { hasNextPage: boolean };
edges: ({ node: { id: string; state: string } | null } | null)[] | null;
};
}
>
>;

test("true", () => undefined);
});
17 changes: 11 additions & 6 deletions packages/api-client-core/src/GadgetRecord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ export enum ChangeTracking {
SinceLastPersisted,
}

export type RecordShape = Record<string, any> | null | undefined | void;
export interface RecordShape {
__typename?: string;
[key: string]: any;
}

/** Represents one record returned from a high level Gadget API call */
export class GadgetRecordImplementation<Shape extends RecordShape> {
Expand All @@ -23,10 +26,12 @@ export class GadgetRecordImplementation<Shape extends RecordShape> {

private empty = false;

constructor(data: Shape) {
this.__gadget.instantiatedFields = cloneDeep(data);
this.__gadget.persistedFields = cloneDeep(data);
Object.assign(this.__gadget.fields, data);
constructor(data?: Shape | null) {
if (data) {
this.__gadget.instantiatedFields = cloneDeep(data);
this.__gadget.persistedFields = cloneDeep(data);
Object.assign(this.__gadget.fields, data);
}

if (!data || Object.keys(data).length === 0) {
this.empty = true;
Expand Down Expand Up @@ -193,6 +198,6 @@ export class GadgetRecordImplementation<Shape extends RecordShape> {
*/

/** Instantiate a `GadgetRecord` with the attributes of your model. A `GadgetRecord` can be used to track changes to your model and persist those changes via Gadget actions. */
export const GadgetRecord: new <Shape extends RecordShape>(data: Shape) => GadgetRecordImplementation<Shape> & Shape =
export const GadgetRecord: new <Shape extends RecordShape>(data?: Shape | null) => GadgetRecordImplementation<Shape> & Shape =
GadgetRecordImplementation as any;
export type GadgetRecord<Shape extends RecordShape> = GadgetRecordImplementation<Shape> & Shape;

0 comments on commit 50f276e

Please sign in to comment.