tantan-typeorm-gs 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +31 -0
- package/bun.lock +300 -0
- package/docs/authentication.md +118 -0
- package/docs/configuration.md +680 -0
- package/docs/contributing.md +177 -0
- package/docs/crud.md +351 -0
- package/docs/custom-client.md +165 -0
- package/docs/development.md +243 -0
- package/docs/entities.md +502 -0
- package/docs/find-options.md +296 -0
- package/docs/google-cloud.md +101 -0
- package/docs/installation.md +51 -0
- package/docs/integration-testing.md +178 -0
- package/docs/limitations.md +223 -0
- package/docs/pagination.md +384 -0
- package/docs/performance.md +171 -0
- package/docs/security.md +153 -0
- package/docs/sorting.md +0 -0
- package/docs/supported-features.md +310 -0
- package/docs/synchronization.md +203 -0
- package/package.json +43 -0
- package/src/client/index.ts +589 -0
- package/src/core/data-source.ts +47 -0
- package/src/core/driver.ts +290 -0
- package/src/core/error.ts +144 -0
- package/src/core/memory.ts +152 -0
- package/src/core/query/interpreter.ts +949 -0
- package/src/core/query/runner.ts +1297 -0
- package/src/core/query/types.ts +155 -0
- package/src/core/schema-builder.ts +64 -0
- package/src/core/types.ts +113 -0
- package/src/core/utils.ts +13 -0
- package/src/index.ts +3 -0
- package/tantan-typeorm-gs.code-workspace +8 -0
- package/test/base/0001-data-source.test.ts +252 -0
- package/test/base/0002-operator.test.ts +616 -0
- package/test/base/0003-select.test.ts +281 -0
- package/test/base/0004-aggregate.test.ts +213 -0
- package/test/base/0005-transcation.test.ts +1566 -0
- package/test/base/0006-relation.test.ts +1611 -0
- package/test/base/0007-logging.test.ts +182 -0
- package/test/base/0008-soft-delete.test.ts +649 -0
- package/test/google-sheets/0001-client.test.ts +1705 -0
- package/test/google-sheets/0002-worksheet-management.test.ts +408 -0
- package/test/google-sheets/0003-data-source.test.ts +1935 -0
- package/test/google-sheets/0004-transactions.test.ts +952 -0
- package/test/google-sheets/0005-operator.test.ts +1124 -0
- package/test/google-sheets/0006-object-criteria.test.ts +283 -0
- package/test/google-sheets/0007-performance.test.ts +532 -0
- package/test/public-api.test.ts +34 -0
- package/tsconfig.build.json +21 -0
- package/tsconfig.json +34 -0
|
@@ -0,0 +1,680 @@
|
|
|
1
|
+
## Configuration
|
|
2
|
+
|
|
3
|
+
`tantan-typeorm-gs` is configured through `createGoogleSheetsDataSource()`.
|
|
4
|
+
|
|
5
|
+
### Basic Configuration
|
|
6
|
+
|
|
7
|
+
A minimal configuration requires:
|
|
8
|
+
|
|
9
|
+
- `type`
|
|
10
|
+
- `spreadsheetId`
|
|
11
|
+
- `credentials`
|
|
12
|
+
|
|
13
|
+
Example:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createGoogleSheetsDataSource } from "tantan-typeorm-gs";
|
|
17
|
+
|
|
18
|
+
const dataSource = createGoogleSheetsDataSource({
|
|
19
|
+
type: "google-sheets",
|
|
20
|
+
|
|
21
|
+
spreadsheetId: process.env.GOOGLE_SHEETS_SPREADSHEET_ID!,
|
|
22
|
+
|
|
23
|
+
credentials: {
|
|
24
|
+
clientEmail: process.env.GOOGLE_SHEETS_CLIENT_EMAIL!,
|
|
25
|
+
|
|
26
|
+
privateKey: process.env.GOOGLE_SHEETS_PRIVATE_KEY!
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
entities: [User]
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Initialize the data source before using repositories:
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
await dataSource.initialize();
|
|
37
|
+
|
|
38
|
+
const repository = dataSource.getRepository(User);
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Configuration Options
|
|
42
|
+
|
|
43
|
+
#### `type`
|
|
44
|
+
|
|
45
|
+
Identifies the database driver.
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
type: "google-sheets";
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
This value is required.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
#### `spreadsheetId`
|
|
56
|
+
|
|
57
|
+
The ID of the Google Spreadsheet used by the driver.
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
spreadsheetId: "1AbCdEfGhIjKlMnOpQrStUvWxYz";
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
This value is required.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
#### `credentials`
|
|
68
|
+
|
|
69
|
+
Google service-account credentials.
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
credentials: {
|
|
73
|
+
clientEmail:
|
|
74
|
+
'service-account@project-id.iam.gserviceaccount.com',
|
|
75
|
+
|
|
76
|
+
privateKey:
|
|
77
|
+
'-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n',
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Both `clientEmail` and `privateKey` are required.
|
|
82
|
+
|
|
83
|
+
For security reasons, credentials should normally come from environment variables or a secret-management system.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
#### `client`
|
|
88
|
+
|
|
89
|
+
A custom `GoogleSheetsClient` implementation can be supplied instead of creating the default Google API client.
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
client: customClient;
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
When `client` is provided, the driver uses that client for Google Sheets operations.
|
|
96
|
+
|
|
97
|
+
This is useful for:
|
|
98
|
+
|
|
99
|
+
- testing
|
|
100
|
+
- mocking
|
|
101
|
+
- custom Google Sheets implementations
|
|
102
|
+
- applications that already manage Google Sheets access themselves
|
|
103
|
+
|
|
104
|
+
The default client is created automatically when `client` is omitted.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
#### `entities`
|
|
109
|
+
|
|
110
|
+
TypeORM entities used by the data source.
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
entities: [User, Product];
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Entities define the worksheet metadata used by the driver.
|
|
117
|
+
|
|
118
|
+
Example:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
@Entity("users")
|
|
122
|
+
class User {
|
|
123
|
+
@PrimaryGeneratedColumn()
|
|
124
|
+
id!: number;
|
|
125
|
+
|
|
126
|
+
@Column()
|
|
127
|
+
name!: string;
|
|
128
|
+
|
|
129
|
+
@Column()
|
|
130
|
+
email!: string;
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
#### `subscribers`
|
|
137
|
+
|
|
138
|
+
TypeORM subscribers can be registered through the data source configuration.
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
subscribers: [UserSubscriber];
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Subscribers can participate in supported TypeORM entity lifecycle events.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
#### `migrations`
|
|
149
|
+
|
|
150
|
+
Migration classes can be supplied through the TypeORM data source configuration.
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
migrations: [
|
|
154
|
+
// migration classes
|
|
155
|
+
];
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Migration support should be considered separately from Google Sheets' own data model because Google Sheets does not provide relational database schema semantics.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
#### `synchronize`
|
|
163
|
+
|
|
164
|
+
Controls TypeORM schema synchronization.
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
synchronize: true;
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
When enabled, TypeORM synchronization is used to create or update the worksheet structure represented by the entity metadata.
|
|
171
|
+
|
|
172
|
+
For production environments, review synchronization behavior carefully before enabling it.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
#### `migrationsRun`
|
|
177
|
+
|
|
178
|
+
Controls whether configured migrations are automatically executed during data-source initialization.
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
migrationsRun: true;
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Use this only when migrations have been configured and the application's migration strategy requires automatic execution.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
#### `dropSchema`
|
|
189
|
+
|
|
190
|
+
Controls whether the configured schema is dropped during initialization.
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
dropSchema: true;
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
This option is destructive and should generally not be enabled against a production spreadsheet.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
#### `logging`
|
|
201
|
+
|
|
202
|
+
TypeORM logging configuration can be passed through to the data source.
|
|
203
|
+
|
|
204
|
+
For example:
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
logging: true;
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Or:
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
logging: ["query", "error"];
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Use logging carefully when credentials or sensitive spreadsheet data may appear in application logs.
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
#### `logger`
|
|
221
|
+
|
|
222
|
+
A custom TypeORM logger can be supplied.
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
logger: customLogger;
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
This allows applications to integrate driver logging with their existing logging infrastructure.
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
#### `name`
|
|
233
|
+
|
|
234
|
+
An optional data-source name.
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
name: "google-sheets";
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
This can be useful when an application manages multiple data sources.
|
|
241
|
+
|
|
242
|
+
### Full Example
|
|
243
|
+
|
|
244
|
+
A configuration can combine the available options:
|
|
245
|
+
|
|
246
|
+
```ts
|
|
247
|
+
const dataSource = createGoogleSheetsDataSource({
|
|
248
|
+
type: "google-sheets",
|
|
249
|
+
|
|
250
|
+
spreadsheetId: process.env.GOOGLE_SHEETS_SPREADSHEET_ID!,
|
|
251
|
+
|
|
252
|
+
credentials: {
|
|
253
|
+
clientEmail: process.env.GOOGLE_SHEETS_CLIENT_EMAIL!,
|
|
254
|
+
|
|
255
|
+
privateKey: process.env.GOOGLE_SHEETS_PRIVATE_KEY!
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
entities: [User, Product],
|
|
259
|
+
|
|
260
|
+
subscribers: [UserSubscriber],
|
|
261
|
+
|
|
262
|
+
synchronize: true,
|
|
263
|
+
|
|
264
|
+
logging: ["query", "error"],
|
|
265
|
+
|
|
266
|
+
name: "google-sheets"
|
|
267
|
+
});
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Custom Client
|
|
271
|
+
|
|
272
|
+
The `client` option takes precedence over the automatically created Google Sheets API client.
|
|
273
|
+
|
|
274
|
+
```text
|
|
275
|
+
client provided
|
|
276
|
+
│
|
|
277
|
+
▼
|
|
278
|
+
use supplied GoogleSheetsClient
|
|
279
|
+
│
|
|
280
|
+
▼
|
|
281
|
+
GoogleSheetsDriver
|
|
282
|
+
|
|
283
|
+
client omitted
|
|
284
|
+
│
|
|
285
|
+
▼
|
|
286
|
+
create GoogleSheetsApiClient
|
|
287
|
+
│
|
|
288
|
+
▼
|
|
289
|
+
GoogleSheetsDriver
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
See the **Custom Client** section for more details.
|
|
293
|
+
|
|
294
|
+
### Lifecycle
|
|
295
|
+
|
|
296
|
+
The typical application lifecycle is:
|
|
297
|
+
|
|
298
|
+
```ts
|
|
299
|
+
const dataSource = createGoogleSheetsDataSource({
|
|
300
|
+
// configuration
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
await dataSource.initialize();
|
|
304
|
+
|
|
305
|
+
const repository = dataSource.getRepository(User);
|
|
306
|
+
|
|
307
|
+
// use repository
|
|
308
|
+
|
|
309
|
+
await dataSource.destroy();
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
The data source should be destroyed when the application no longer needs it, especially in scripts, tests, and short-lived processes.
|
|
313
|
+
|
|
314
|
+
## Entity Definition
|
|
315
|
+
|
|
316
|
+
`tantan-typeorm-gs` uses standard TypeORM entity definitions to describe the structure of Google Sheets data.
|
|
317
|
+
|
|
318
|
+
An entity represents a worksheet, while entity columns represent worksheet columns.
|
|
319
|
+
|
|
320
|
+
### Basic Entity
|
|
321
|
+
|
|
322
|
+
A basic entity can be defined using TypeORM decorators:
|
|
323
|
+
|
|
324
|
+
```ts
|
|
325
|
+
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
|
|
326
|
+
|
|
327
|
+
@Entity("users")
|
|
328
|
+
class User {
|
|
329
|
+
@PrimaryGeneratedColumn()
|
|
330
|
+
id!: number;
|
|
331
|
+
|
|
332
|
+
@Column()
|
|
333
|
+
name!: string;
|
|
334
|
+
|
|
335
|
+
@Column()
|
|
336
|
+
email!: string;
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
In this example:
|
|
341
|
+
|
|
342
|
+
```text
|
|
343
|
+
Entity
|
|
344
|
+
User
|
|
345
|
+
↓
|
|
346
|
+
Worksheet
|
|
347
|
+
users
|
|
348
|
+
|
|
349
|
+
Columns
|
|
350
|
+
id
|
|
351
|
+
name
|
|
352
|
+
email
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
The worksheet should contain a header row corresponding to the entity columns.
|
|
356
|
+
|
|
357
|
+
For example:
|
|
358
|
+
|
|
359
|
+
```text
|
|
360
|
+
| id | name | email |
|
|
361
|
+
|----|------|-------|
|
|
362
|
+
| 1 | Budi | budi@example.com |
|
|
363
|
+
| 2 | Siti | siti@example.com |
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Worksheet Name
|
|
367
|
+
|
|
368
|
+
The worksheet name can be specified with `@Entity()`:
|
|
369
|
+
|
|
370
|
+
```ts
|
|
371
|
+
@Entity("users")
|
|
372
|
+
class User {
|
|
373
|
+
// ...
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
The value passed to `@Entity()` is used as the entity's custom table name and therefore determines the worksheet name used by the driver.
|
|
378
|
+
|
|
379
|
+
If no explicit name is supplied:
|
|
380
|
+
|
|
381
|
+
```ts
|
|
382
|
+
@Entity()
|
|
383
|
+
class User {
|
|
384
|
+
// ...
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
TypeORM metadata determines the resulting table name.
|
|
389
|
+
|
|
390
|
+
### Columns
|
|
391
|
+
|
|
392
|
+
Entity properties are mapped to worksheet columns using `@Column()`:
|
|
393
|
+
|
|
394
|
+
```ts
|
|
395
|
+
@Column()
|
|
396
|
+
name!: string;
|
|
397
|
+
|
|
398
|
+
@Column()
|
|
399
|
+
email!: string;
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
The property name is used as the column name unless TypeORM metadata specifies another database column name.
|
|
403
|
+
|
|
404
|
+
A custom column name can be defined:
|
|
405
|
+
|
|
406
|
+
```ts
|
|
407
|
+
@Column({
|
|
408
|
+
name: 'full_name',
|
|
409
|
+
})
|
|
410
|
+
name!: string;
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
The worksheet header then uses:
|
|
414
|
+
|
|
415
|
+
```text
|
|
416
|
+
full_name
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
instead of:
|
|
420
|
+
|
|
421
|
+
```text
|
|
422
|
+
name
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Primary Column
|
|
426
|
+
|
|
427
|
+
A manually assigned primary key can be defined with `@PrimaryColumn()`:
|
|
428
|
+
|
|
429
|
+
```ts
|
|
430
|
+
@Entity("users")
|
|
431
|
+
class User {
|
|
432
|
+
@PrimaryColumn()
|
|
433
|
+
id!: number;
|
|
434
|
+
|
|
435
|
+
@Column()
|
|
436
|
+
name!: string;
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
When inserting a row, the primary key must be provided.
|
|
441
|
+
|
|
442
|
+
For example:
|
|
443
|
+
|
|
444
|
+
```ts
|
|
445
|
+
await repository.insert({
|
|
446
|
+
id: 1,
|
|
447
|
+
name: "Budi"
|
|
448
|
+
});
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
An insert without the required primary key is rejected by the driver.
|
|
452
|
+
|
|
453
|
+
### Generated Primary Column
|
|
454
|
+
|
|
455
|
+
Auto-generated primary keys can be defined with `@PrimaryGeneratedColumn()`:
|
|
456
|
+
|
|
457
|
+
```ts
|
|
458
|
+
@Entity("users")
|
|
459
|
+
class User {
|
|
460
|
+
@PrimaryGeneratedColumn()
|
|
461
|
+
id!: number;
|
|
462
|
+
|
|
463
|
+
@Column()
|
|
464
|
+
name!: string;
|
|
465
|
+
}
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
The default generated strategy is supported by the driver.
|
|
469
|
+
|
|
470
|
+
When inserting multiple rows:
|
|
471
|
+
|
|
472
|
+
```ts
|
|
473
|
+
await repository.insert([
|
|
474
|
+
{
|
|
475
|
+
name: "Budi"
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
name: "Siti"
|
|
479
|
+
}
|
|
480
|
+
]);
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
The driver assigns generated IDs to rows that do not provide an explicit generated primary key.
|
|
484
|
+
|
|
485
|
+
### Explicit Generated Primary Keys
|
|
486
|
+
|
|
487
|
+
An explicitly supplied generated primary key is preserved:
|
|
488
|
+
|
|
489
|
+
```ts
|
|
490
|
+
await repository.insert({
|
|
491
|
+
id: 100,
|
|
492
|
+
name: "Budi"
|
|
493
|
+
});
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
The driver does not replace the supplied ID with another generated value.
|
|
497
|
+
|
|
498
|
+
If the explicit primary key already exists in the worksheet, the insert is rejected as a duplicate primary-key operation.
|
|
499
|
+
|
|
500
|
+
### UUID Primary Keys
|
|
501
|
+
|
|
502
|
+
UUID generation is supported through TypeORM's generated primary-column metadata:
|
|
503
|
+
|
|
504
|
+
```ts
|
|
505
|
+
@Entity("users")
|
|
506
|
+
class User {
|
|
507
|
+
@PrimaryGeneratedColumn("uuid")
|
|
508
|
+
id!: string;
|
|
509
|
+
|
|
510
|
+
@Column()
|
|
511
|
+
name!: string;
|
|
512
|
+
}
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
When the ID is omitted during insertion, the driver generates a UUID.
|
|
516
|
+
|
|
517
|
+
```ts
|
|
518
|
+
await repository.insert({
|
|
519
|
+
name: "Budi"
|
|
520
|
+
});
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
### Unsupported Generated Strategies
|
|
524
|
+
|
|
525
|
+
Not every relational database generation strategy has an equivalent in Google Sheets.
|
|
526
|
+
|
|
527
|
+
The driver does not support generated primary-key strategies that depend on database-specific row identity mechanisms, such as:
|
|
528
|
+
|
|
529
|
+
```ts
|
|
530
|
+
@PrimaryGeneratedColumn('identity')
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
and:
|
|
534
|
+
|
|
535
|
+
```ts
|
|
536
|
+
@PrimaryGeneratedColumn('rowid')
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
These strategies are rejected by the driver rather than being emulated.
|
|
540
|
+
|
|
541
|
+
### Dates
|
|
542
|
+
|
|
543
|
+
TypeORM date metadata can be used for supported entity date columns.
|
|
544
|
+
|
|
545
|
+
Lifecycle-managed date columns such as create-date and update-date columns are populated by the driver during insert operations when the corresponding TypeORM metadata is present.
|
|
546
|
+
|
|
547
|
+
Example:
|
|
548
|
+
|
|
549
|
+
```ts
|
|
550
|
+
@Entity("users")
|
|
551
|
+
class User {
|
|
552
|
+
@PrimaryGeneratedColumn()
|
|
553
|
+
id!: number;
|
|
554
|
+
|
|
555
|
+
@Column()
|
|
556
|
+
name!: string;
|
|
557
|
+
|
|
558
|
+
@CreateDateColumn()
|
|
559
|
+
createdAt!: Date;
|
|
560
|
+
|
|
561
|
+
@UpdateDateColumn()
|
|
562
|
+
updatedAt!: Date;
|
|
563
|
+
}
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
### Relations
|
|
567
|
+
|
|
568
|
+
Relations can be defined using normal TypeORM relation decorators:
|
|
569
|
+
|
|
570
|
+
```ts
|
|
571
|
+
@Entity("posts")
|
|
572
|
+
class Post {
|
|
573
|
+
@PrimaryGeneratedColumn()
|
|
574
|
+
id!: number;
|
|
575
|
+
|
|
576
|
+
@Column()
|
|
577
|
+
title!: string;
|
|
578
|
+
|
|
579
|
+
@Column()
|
|
580
|
+
authorId!: number;
|
|
581
|
+
|
|
582
|
+
@ManyToOne(() => User, {
|
|
583
|
+
nullable: false
|
|
584
|
+
})
|
|
585
|
+
author!: User;
|
|
586
|
+
}
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
The driver supports relation metadata resolution and supported relation loading.
|
|
590
|
+
|
|
591
|
+
However, Google Sheets does not provide database-level foreign-key constraints or referential-integrity enforcement.
|
|
592
|
+
|
|
593
|
+
Therefore, defining a TypeORM relation does not create a foreign-key constraint inside Google Sheets.
|
|
594
|
+
|
|
595
|
+
### Naming Strategy
|
|
596
|
+
|
|
597
|
+
TypeORM naming strategies can affect the names generated from entity metadata.
|
|
598
|
+
|
|
599
|
+
For example, a naming strategy may transform a column name:
|
|
600
|
+
|
|
601
|
+
```text
|
|
602
|
+
displayName
|
|
603
|
+
↓
|
|
604
|
+
col_displayname
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
The driver uses the resulting TypeORM metadata when resolving worksheet and column names.
|
|
608
|
+
|
|
609
|
+
This allows applications to continue using their existing TypeORM naming strategy where supported.
|
|
610
|
+
|
|
611
|
+
### Entity Metadata
|
|
612
|
+
|
|
613
|
+
The driver relies on TypeORM metadata rather than requiring a separate Google Sheets schema definition.
|
|
614
|
+
|
|
615
|
+
The resulting metadata determines information such as:
|
|
616
|
+
|
|
617
|
+
- worksheet name
|
|
618
|
+
- column names
|
|
619
|
+
- primary columns
|
|
620
|
+
- generated columns
|
|
621
|
+
- create-date columns
|
|
622
|
+
- update-date columns
|
|
623
|
+
- relations
|
|
624
|
+
- naming-strategy transformations
|
|
625
|
+
|
|
626
|
+
This keeps entity definitions compatible with the normal TypeORM programming model.
|
|
627
|
+
|
|
628
|
+
### Example
|
|
629
|
+
|
|
630
|
+
A complete entity can look like:
|
|
631
|
+
|
|
632
|
+
```ts
|
|
633
|
+
import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from "typeorm";
|
|
634
|
+
|
|
635
|
+
@Entity("users")
|
|
636
|
+
class User {
|
|
637
|
+
@PrimaryGeneratedColumn()
|
|
638
|
+
id!: number;
|
|
639
|
+
|
|
640
|
+
@Column()
|
|
641
|
+
name!: string;
|
|
642
|
+
|
|
643
|
+
@Column()
|
|
644
|
+
email!: string;
|
|
645
|
+
|
|
646
|
+
@CreateDateColumn()
|
|
647
|
+
createdAt!: Date;
|
|
648
|
+
|
|
649
|
+
@UpdateDateColumn()
|
|
650
|
+
updatedAt!: Date;
|
|
651
|
+
}
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
Register the entity with the data source:
|
|
655
|
+
|
|
656
|
+
```ts
|
|
657
|
+
const dataSource = createGoogleSheetsDataSource({
|
|
658
|
+
type: "google-sheets",
|
|
659
|
+
|
|
660
|
+
spreadsheetId: process.env.GOOGLE_SHEETS_SPREADSHEET_ID!,
|
|
661
|
+
|
|
662
|
+
credentials: {
|
|
663
|
+
clientEmail: process.env.GOOGLE_SHEETS_CLIENT_EMAIL!,
|
|
664
|
+
|
|
665
|
+
privateKey: process.env.GOOGLE_SHEETS_PRIVATE_KEY!
|
|
666
|
+
},
|
|
667
|
+
|
|
668
|
+
entities: [User]
|
|
669
|
+
});
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
After initialization, the repository can be obtained normally:
|
|
673
|
+
|
|
674
|
+
```ts
|
|
675
|
+
await dataSource.initialize();
|
|
676
|
+
|
|
677
|
+
const repository = dataSource.getRepository(User);
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
The repository can then be used for CRUD operations against the corresponding worksheet.
|