Skip to content

Commit 0671e66

Browse files
committed
新增 Airtable 相關的 DTO 介面,並重構 BaseService 和 HttpService 以支援 API 操作
1 parent 45a6609 commit 0671e66

10 files changed

+233
-108
lines changed

src/dtos/AirTableCreateMapping.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export interface AirtableCreateMapping {
2+
id: string;
3+
fields: Record<string, unknown>;
4+
createdTime: string;
5+
}

src/dtos/AirTableDeleteMapping.ts

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface AirtableDeleteMapping {
2+
id: string;
3+
deleted: boolean;
4+
}

src/dtos/AirTableListMapping.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export interface AirtableListMapping {
2+
offset?: string;
3+
records: AirtableRecord[];
4+
}
5+
6+
export interface AirtableRecord {
7+
id: string;
8+
fields: Record<string, unknown>;
9+
createdTime: string;
10+
commentCount?: number;
11+
}

src/dtos/SelectOptions.ts

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export interface SelectOptions {
2+
fields?: string[];
3+
filterByFormula?: string;
4+
maxRecords?: number;
5+
pageSize?: number;
6+
sort?: string;
7+
view?: string;
8+
}

src/dtos/airTableGetMapping.ts

-11
This file was deleted.

src/dtos/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export * from './AirTableCreateMapping.js';
2+
export * from './AirTableListMapping.js';
3+
export * from './AirTableDeleteMapping.js';
4+
export * from './SelectOptions.js';

src/services/BaseService.ts

+37-17
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,37 @@
1-
import AsyncAirtable from 'asyncairtable';
2-
import { AirtableRecord, DeleteResponse, SelectOptions } from 'asyncairtable/lib/@types';
31
import { BaseModel } from '@thomascsd/stools-models';
2+
import { HttpService } from './HttpService.js';
3+
import { AirtableDeleteMapping, AirtableRecord, SelectOptions } from '../dtos/index.js';
44

55
/**
6-
* Base class that access AitTable api
6+
* Base class that accesses Airtable API
77
*
88
* @export
99
* @class BaseService
1010
*/
1111
export class BaseService {
12-
constructor(public apiKey: string) {}
12+
/**
13+
* Creates an instance of BaseService.
14+
*/
15+
constructor() {}
1316

17+
/**
18+
* Retrieves records from a specified Airtable base and table.
19+
*
20+
* @template T
21+
* @param {string} baseId - The ID of the Airtable base.
22+
* @param {string} tableName - The name of the table.
23+
* @param {SelectOptions} [options] - Optional query parameters.
24+
* @returns {Promise<T[]>} - A promise that resolves to an array of records.
25+
*/
1426
protected async get<T extends BaseModel>(
27+
token: string,
1528
baseId: string,
1629
tableName: string,
1730
options?: SelectOptions
1831
): Promise<T[]> {
19-
const airtable = this.getAirTableClient(baseId);
20-
const records: AirtableRecord[] = await airtable.select(tableName, options);
32+
const airtable = this.getAirTableClient(token, baseId);
33+
const mapping = await airtable.list(tableName);
34+
const records = mapping.records;
2135

2236
const body = records
2337
.map((o: AirtableRecord) => {
@@ -33,18 +47,23 @@ export class BaseService {
3347
return body;
3448
}
3549

36-
protected async save<T extends BaseModel>(baseId: string, tableName: string, model: T) {
37-
const airtable = this.getAirTableClient(baseId);
38-
const body = await airtable.createRecord(tableName, model);
50+
protected async save<T extends BaseModel>(
51+
token: string,
52+
baseId: string,
53+
tableName: string,
54+
model: T
55+
) {
56+
const airtable = this.getAirTableClient(token, baseId);
57+
const body = await airtable.create(tableName, model);
3958
return body;
4059
}
4160

42-
async update<T extends BaseModel>(baseId: string, tableName: string, model: T) {
43-
const airtable = this.getAirTableClient(baseId);
61+
async update<T extends BaseModel>(token: string, baseId: string, tableName: string, model: T) {
62+
const airtable = this.getAirTableClient(token, baseId);
4463
const tmpModel = { ...model };
4564
const id = tmpModel.id;
4665
delete tmpModel.id;
47-
const body = await airtable.updateRecord(tableName, {
66+
const body = await airtable.update(tableName, model.id as string, {
4867
id: id as string,
4968
fields: tmpModel,
5069
});
@@ -53,18 +72,19 @@ export class BaseService {
5372
}
5473

5574
protected async delete<T extends BaseModel>(
75+
token: string,
5676
baseId: string,
5777
tableName: string,
5878
model: T
59-
): Promise<DeleteResponse> {
60-
const airtable = this.getAirTableClient(baseId);
79+
): Promise<AirtableDeleteMapping> {
80+
const airtable = this.getAirTableClient(token, baseId);
6181

62-
const res = await airtable.deleteRecord(tableName, model.id as string);
82+
const res = await airtable.delete(tableName, model.id as string);
6383
return res;
6484
}
6585

66-
private getAirTableClient(baseId: string) {
67-
const airtable = new AsyncAirtable(this.apiKey, baseId);
86+
private getAirTableClient(token: string, baseId: string) {
87+
const airtable = new HttpService(token, baseId);
6888

6989
return airtable;
7090
}

src/services/DataFunctionService.ts

+17-11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { AirtableRecord, DeleteResponse, SelectOptions } from 'asyncairtable/lib/@types';
21
import { BaseModel } from '@thomascsd/stools-models';
3-
import { BaseService } from './BaseService';
2+
import { BaseService } from './BaseService.js';
3+
import { AirtableCreateMapping, AirtableDeleteMapping, SelectOptions } from '../dtos/index.js';
44

55
/**
66
* Defines service that access AirTable's data
@@ -10,23 +10,29 @@ import { BaseService } from './BaseService';
1010
* @extends {BaseService}
1111
*/
1212
export class DataFunctionService extends BaseService {
13-
constructor(public apiKey: string, public baseId: string) {
14-
super(apiKey);
13+
constructor(public token: string, public baseId: string) {
14+
super();
1515
}
1616

1717
async getDatas<T extends BaseModel>(tableName: string, options?: SelectOptions): Promise<T[]> {
18-
return await super.get<T>(this.baseId, tableName, options);
18+
return await super.get<T>(this.token, this.baseId, tableName, options);
1919
}
2020

21-
async saveData<T extends BaseModel>(tableName: string, model: T): Promise<AirtableRecord> {
22-
return await super.save<T>(this.baseId, tableName, model);
21+
async saveData<T extends BaseModel>(tableName: string, model: T): Promise<AirtableCreateMapping> {
22+
return await super.save<T>(this.token, this.baseId, tableName, model);
2323
}
2424

25-
async updateData<T extends BaseModel>(tableName: string, model: T): Promise<AirtableRecord> {
26-
return await super.update<T>(this.baseId, tableName, model);
25+
async updateData<T extends BaseModel>(
26+
tableName: string,
27+
model: T
28+
): Promise<AirtableCreateMapping> {
29+
return await super.update<T>(this.token, this.baseId, tableName, model);
2730
}
2831

29-
async deleteData<T extends BaseModel>(tableName: string, model: T): Promise<DeleteResponse> {
30-
return await super.delete<T>(this.baseId, tableName, model);
32+
async deleteData<T extends BaseModel>(
33+
tableName: string,
34+
model: T
35+
): Promise<AirtableDeleteMapping> {
36+
return await super.delete<T>(this.token, this.baseId, tableName, model);
3137
}
3238
}

src/services/DataService.ts

+55-58
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,19 @@
1-
import { AirtableRecord, DeleteResponse, SelectOptions } from 'asyncairtable/lib/@types';
2-
import { Service, Inject, Token } from 'typedi';
1+
import { Service } from 'typedi';
32
import { BaseModel } from '@thomascsd/stools-models';
4-
import { BaseService } from './BaseService';
5-
6-
const AIRTABLE_APIKEY_TOKEN = 'stools_AIRTABLE_APIKEY_TOKEN';
7-
8-
/**
9-
* Defines a token that get Airtable key
10-
*/
11-
export const API_KEY_TOKEN = new Token<string>(AIRTABLE_APIKEY_TOKEN);
3+
import { BaseService } from './BaseService.js';
4+
import { AirtableCreateMapping, AirtableDeleteMapping, SelectOptions } from '../dtos/index.js';
125

136
/**
14-
* Defines service that access AirTable's data, and use DI with typedi.
15-
*
7+
* Defines service that accesses AirTable's data, and uses DI with typedi.
8+
*
169
* ```typescript
1710
* import { Service, Container } from 'typedi';
1811
* import { DataService, BaseModel, API_KEY_TOKEN } from '@thomascsd/stools';
19-
12+
*
2013
* Container.set(API_KEY_TOKEN, process.env.<your api key>);
21-
14+
*
2215
* const BASE_ID = '<your base id>';
23-
16+
*
2417
* export class Contact extends BaseModel {
2518
* name: string;
2619
* email: string;
@@ -29,105 +22,109 @@ export const API_KEY_TOKEN = new Token<string>(AIRTABLE_APIKEY_TOKEN);
2922
*
3023
* @Service()
3124
* export class ContactService {
32-
* constructor(private db: DataService) {}
25+
* constructor(private db: DataService) {}
3326
*
34-
* async getContacts(): Promise<Contact[]> {
35-
* return await this.db.getDatas<Contact>(BASE_ID, '<your table name of AirTable>');
36-
* }
27+
* async getContacts(): Promise<Contact[]> {
28+
* return await this.db.getDatas<Contact>(BASE_ID, '<your table name of AirTable>');
29+
* }
3730
*
38-
* async saveContact(contact: Contact) {
39-
* return await this.db.saveData<Contact>(BASE_ID, '<your table name of AirTable>', contact);
40-
* }
31+
* async saveContact(contact: Contact) {
32+
* return await this.db.saveData<Contact>(BASE_ID, '<your table name of AirTable>', contact);
33+
* }
4134
*
42-
* async updateContact(contact: Contact) {
43-
* return await this.db.updateData<Contact>(BASE_ID, '<your table name of AirTable>', contact);
44-
* }
35+
* async updateContact(contact: Contact) {
36+
* return await this.db.updateData<Contact>(BASE_ID, '<your table name of AirTable>', contact);
37+
* }
4538
* }
4639
* ```
47-
*
40+
*
4841
* @export
4942
* @class DataService
5043
* @extends {BaseService}
5144
*/
5245
@Service()
5346
export class DataService extends BaseService {
54-
constructor(@Inject(API_KEY_TOKEN) public apiKey: string) {
55-
super(apiKey);
56-
57-
if (!this.apiKey) {
58-
this.apiKey = process.env.AIRTABLE_API ?? '';
59-
}
47+
constructor() {
48+
super();
6049
}
6150

6251
/**
63-
* Get Datas from AirTable
52+
* Get data from AirTable
6453
*
6554
* @template T
66-
* @param {string} baseId
67-
* @param {string} tableName
68-
* @param {SelectOptions} [options]
69-
* @return {*} {Promise<T[]>}
55+
* @param {string} token - API token
56+
* @param {string} baseId - Base ID of AirTable
57+
* @param {string} tableName - Table name in AirTable
58+
* @param {SelectOptions} [options] - Options for selecting data
59+
* @return {Promise<T[]>} - Promise resolving to an array of data
7060
* @memberof DataService
7161
*/
7262
async getDatas<T extends BaseModel>(
63+
token: string,
7364
baseId: string,
7465
tableName: string,
7566
options?: SelectOptions
7667
): Promise<T[]> {
77-
return await super.get<T>(baseId, tableName, options);
68+
return await super.get<T>(token, baseId, tableName, options);
7869
}
7970

8071
/**
81-
* Insert data to Airtable
72+
* Insert data into AirTable
8273
*
8374
* @template T
84-
* @param {string} baseId
85-
* @param {string} tableName
86-
* @param {T} model
87-
* @return {*} {Promise<AirtableRecord>}
75+
* @param {string} token - API token
76+
* @param {string} baseId - Base ID of AirTable
77+
* @param {string} tableName - Table name in AirTable
78+
* @param {T} model - Data model to insert
79+
* @return {Promise<AirtableCreateMapping>} - Promise resolving to the created record mapping
8880
* @memberof DataService
8981
*/
9082
async saveData<T extends BaseModel>(
83+
token: string,
9184
baseId: string,
9285
tableName: string,
9386
model: T
94-
): Promise<AirtableRecord> {
95-
return await super.save<T>(baseId, tableName, model);
87+
): Promise<AirtableCreateMapping> {
88+
return await super.save<T>(token, baseId, tableName, model);
9689
}
9790

9891
/**
99-
* Update data to AirTable
92+
* Update data in AirTable
10093
*
10194
* @template T
102-
* @param {string} baseId
103-
* @param {string} tableName
104-
* @param {T} model
105-
* @return {*} {Promise<AirtableRecord>}
95+
* @param {string} token - API token
96+
* @param {string} baseId - Base ID of AirTable
97+
* @param {string} tableName - Table name in AirTable
98+
* @param {T} model - Data model to update
99+
* @return {Promise<AirtableCreateMapping>} - Promise resolving to the updated record mapping
106100
* @memberof DataService
107101
*/
108102
async updateData<T extends BaseModel>(
103+
token: string,
109104
baseId: string,
110105
tableName: string,
111106
model: T
112-
): Promise<AirtableRecord> {
113-
return await super.update<T>(baseId, tableName, model);
107+
): Promise<AirtableCreateMapping> {
108+
return await super.update<T>(token, baseId, tableName, model);
114109
}
115110

116111
/**
117112
* Delete data from AirTable
118113
*
119114
* @template T
120-
* @param {string} baseId
121-
* @param {string} tableName
122-
* @param {T} model
123-
* @return {*} {Promise<DeleteResponse>}
115+
* @param {string} token - API token
116+
* @param {string} baseId - Base ID of AirTable
117+
* @param {string} tableName - Table name in AirTable
118+
* @param {T} model - Data model to delete
119+
* @return {Promise<AirtableDeleteMapping>} - Promise resolving to the deleted record mapping
124120
* @memberof DataService
125121
*/
126122
async deleteData<T extends BaseModel>(
123+
token: string,
127124
baseId: string,
128125
tableName: string,
129126
model: T
130-
): Promise<DeleteResponse> {
131-
return await super.delete<T>(baseId, tableName, model);
127+
): Promise<AirtableDeleteMapping> {
128+
return await super.delete<T>(token, baseId, tableName, model);
132129
}
133130
}

0 commit comments

Comments
 (0)