Skip to content

Commit

Permalink
Make record data non-nullable
Browse files Browse the repository at this point in the history
  • Loading branch information
Vladimir Ubogovich committed Aug 11, 2021
1 parent 349f533 commit f434e51
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 86 deletions.
8 changes: 8 additions & 0 deletions .changeset/pretty-tigers-turn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@qualifyze/airtable": major
---

[BREAKING CHANGE] Make AirtableRecord.data non-nullable.
It's always an object once any operation is executed, and it's inconvenient
to cast it to non-nullable in the calling code. The consumers should check
their code for `AirtableRecord` constructor or `data` property usage.
4 changes: 2 additions & 2 deletions integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const main = async () => {
console.log("Checking record.fetch...");

validateRecord(
await new AirtableRecord(table, singleRecord.id).fetch(),
await new AirtableRecord(table, singleRecord.id, {}).fetch(),
singleRecord
);

Expand All @@ -98,7 +98,7 @@ const main = async () => {
console.log("Checking with record.fetch for non-existing record...");

await validateNotFound(() =>
new AirtableRecord(table, singleRecord.id).fetch()
new AirtableRecord(table, singleRecord.id, {}).fetch()
);
}

Expand Down
11 changes: 10 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"devDependencies": {
"@changesets/changelog-github": "^0.4.0",
"@changesets/cli": "^2.16.0",
"@types/airtable": "^0.10.1",
"@typescript-eslint/eslint-plugin": "^4.28.3",
"@typescript-eslint/parser": "^4.28.2",
"airtable": "^0.10.1",
Expand Down
81 changes: 81 additions & 0 deletions src/record-draft.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { FieldsValidator, UnknownFields } from "./fields";
import { ActionPointOptions } from "./action-point";
import { RestMethod, UnknownActionPayload } from "./endpoint";
import {
RecordDataValidation,
DeletedRecord,
DeletedRecordValidation,
} from "./raw-types";
import { TableActionPoint } from "./table";
import { AirtableRecord } from "./record";

export type RecordDataSource<Fields extends UnknownFields> = TableActionPoint &
FieldsValidator<Fields>;

export interface RecordActionPoint {
runRecordAction<P extends UnknownActionPayload, R>(
method: RestMethod,
{ path, ...options }: ActionPointOptions<P, R>
): Promise<R>;
}

export class AirtableRecordDraft<Fields extends UnknownFields>
implements RecordActionPoint
{
public readonly source: RecordDataSource<Fields>;
public readonly id: string;

constructor(source: RecordDataSource<Fields>, id: string) {
this.source = source;
this.id = id;
}

runRecordAction<P extends UnknownActionPayload, R>(
method: RestMethod,
{ path, ...options }: ActionPointOptions<P, R>
): Promise<R> {
return this.source.runTableAction(method, {
path: path ? `${this.id}/${path}` : this.id,
...options,
});
}

async fetch(): Promise<AirtableRecord<Fields>> {
const { fields } = await this.runRecordAction("GET", {
responseValidation: new RecordDataValidation(this.source),
});
return new AirtableRecord(this.source, this.id, fields);
}

async update(data: Fields): Promise<AirtableRecord<Fields>> {
const { id, fields } = await this.runRecordAction("PATCH", {
responseValidation: new RecordDataValidation(this.source),
payload: {
body: {
fields: data,
},
},
});

return new AirtableRecord(this.source, id, fields);
}

async replace(data: Fields): Promise<AirtableRecord<Fields>> {
const { id, fields } = await this.runRecordAction("PUT", {
responseValidation: new RecordDataValidation(this.source),
payload: {
body: {
fields: data,
},
},
});

return new AirtableRecord(this.source, id, fields);
}

destroy(): Promise<DeletedRecord> {
return this.runRecordAction("DELETE", {
responseValidation: new DeletedRecordValidation(),
});
}
}
88 changes: 9 additions & 79 deletions src/record.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,11 @@
import { FieldsValidator, UnknownFields } from "./fields";
import { ActionPointOptions } from "./action-point";
import { RestMethod, UnknownActionPayload } from "./endpoint";
import {
RecordData,
RecordDataValidation,
DeletedRecord,
DeletedRecordValidation,
MultiRecordData,
} from "./raw-types";
import { TableActionPoint } from "./table";
import { UnknownFields } from "./fields";
import { RecordData, MultiRecordData } from "./raw-types";
import { AirtableRecordDraft, RecordDataSource } from "./record-draft";

export type RecordDataSource<Fields extends UnknownFields> = TableActionPoint &
FieldsValidator<Fields>;

export interface RecordActionPoint {
runRecordAction<P extends UnknownActionPayload, R>(
method: RestMethod,
{ path, ...options }: ActionPointOptions<P, R>
): Promise<R>;
}

export class AirtableRecord<Fields extends UnknownFields>
implements RecordActionPoint
{
public readonly source: RecordDataSource<Fields>;
public readonly id: string;
public readonly data?: Readonly<Fields>;
export class AirtableRecord<
Fields extends UnknownFields
> extends AirtableRecordDraft<Fields> {
public readonly data: Readonly<Fields>;

static fromRecordData<Fields extends UnknownFields>(
source: RecordDataSource<Fields>,
Expand All @@ -41,58 +21,8 @@ export class AirtableRecord<Fields extends UnknownFields>
return records.map((data) => this.fromRecordData(source, data));
}

constructor(source: RecordDataSource<Fields>, id: string, data?: Fields) {
this.source = source;
this.id = id;
constructor(source: RecordDataSource<Fields>, id: string, data: Fields) {
super(source, id);
this.data = data;
}

runRecordAction<P extends UnknownActionPayload, R>(
method: RestMethod,
{ path, ...options }: ActionPointOptions<P, R>
): Promise<R> {
return this.source.runTableAction(method, {
path: path ? `${this.id}/${path}` : this.id,
...options,
});
}

async fetch(): Promise<AirtableRecord<Fields>> {
const { fields } = await this.runRecordAction("GET", {
responseValidation: new RecordDataValidation(this.source),
});
return new AirtableRecord(this.source, this.id, fields);
}

async update(data: Fields): Promise<AirtableRecord<Fields>> {
const { id, fields } = await this.runRecordAction("PATCH", {
responseValidation: new RecordDataValidation(this.source),
payload: {
body: {
fields: data,
},
},
});

return new AirtableRecord(this.source, id, fields);
}

async replace(data: Fields): Promise<AirtableRecord<Fields>> {
const { id, fields } = await this.runRecordAction("PUT", {
responseValidation: new RecordDataValidation(this.source),
payload: {
body: {
fields: data,
},
},
});

return new AirtableRecord(this.source, id, fields);
}

destroy(): Promise<DeletedRecord> {
return this.runRecordAction("DELETE", {
responseValidation: new DeletedRecordValidation(),
});
}
}
9 changes: 5 additions & 4 deletions src/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ActionPoint, ActionPointOptions } from "./action-point";
import { ValidationContext } from "./validator";
import { RestMethod, UnknownActionPayload } from "./endpoint";
import { AirtableRecord } from "./record";
import { AirtableRecordDraft } from "./record-draft";
import { SelectQuery, SelectQueryParams } from "./select-query";

import { RecordData, RecordDataValidation } from "./raw-types";
Expand Down Expand Up @@ -70,7 +71,7 @@ export class Table<Fields extends UnknownFields>
}

find(recordId: string): Promise<AirtableRecord<Fields>> {
return new AirtableRecord(this, recordId).fetch();
return new AirtableRecordDraft(this, recordId).fetch();
}

select(query: SelectQueryParams<Fields> = {}): SelectQuery<Fields> {
Expand Down Expand Up @@ -118,7 +119,7 @@ export class Table<Fields extends UnknownFields>
private async updateSingleRecord(
data: RecordData<Fields>
): Promise<AirtableRecord<Fields>> {
return new AirtableRecord(this, data.id).update(data.fields);
return new AirtableRecordDraft(this, data.id).update(data.fields);
}

private async updateMultipleRecords(
Expand All @@ -137,7 +138,7 @@ export class Table<Fields extends UnknownFields>
private async replaceSingleRecord(
data: RecordData<Fields>
): Promise<AirtableRecord<Fields>> {
return new AirtableRecord(this, data.id).replace(data.fields);
return new AirtableRecordDraft(this, data.id).replace(data.fields);
}

private async replaceMultipleRecords(
Expand Down Expand Up @@ -173,7 +174,7 @@ export class Table<Fields extends UnknownFields>
}

private destroySingleRecord(id: string): Promise<DeletedRecord> {
return new AirtableRecord(this, id).destroy();
return new AirtableRecordDraft(this, id).destroy();
}

private async destroyMultipleRecords(
Expand Down

0 comments on commit f434e51

Please sign in to comment.