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,243 @@
|
|
|
1
|
+
## Development Guide
|
|
2
|
+
|
|
3
|
+
This guide describes the development workflow for contributing to `tantan-typeorm-gs`.
|
|
4
|
+
|
|
5
|
+
### Requirements
|
|
6
|
+
|
|
7
|
+
The project uses:
|
|
8
|
+
|
|
9
|
+
- Bun
|
|
10
|
+
- TypeScript
|
|
11
|
+
- TypeORM
|
|
12
|
+
- Google Sheets API
|
|
13
|
+
|
|
14
|
+
Install the project dependencies:
|
|
15
|
+
|
|
16
|
+
```bash id="7m2x4p"
|
|
17
|
+
bun install
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Project Structure
|
|
21
|
+
|
|
22
|
+
The project is organized around several main layers:
|
|
23
|
+
|
|
24
|
+
```text id="5q8n1v"
|
|
25
|
+
src/
|
|
26
|
+
├── client/
|
|
27
|
+
│ └── index.ts
|
|
28
|
+
│
|
|
29
|
+
└── core/
|
|
30
|
+
├── driver.ts
|
|
31
|
+
├── memory.ts
|
|
32
|
+
├── data-source.ts
|
|
33
|
+
├── schema-builder.ts
|
|
34
|
+
├── types.ts
|
|
35
|
+
├── utils.ts
|
|
36
|
+
└── query/
|
|
37
|
+
├── interpreter.ts
|
|
38
|
+
├── runner.ts
|
|
39
|
+
└── types.ts
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The main execution flow is:
|
|
45
|
+
|
|
46
|
+
```text id="3v7k9c"
|
|
47
|
+
TypeORM Repository
|
|
48
|
+
↓
|
|
49
|
+
QueryBuilder
|
|
50
|
+
↓
|
|
51
|
+
GoogleSheetsQueryRunner
|
|
52
|
+
↓
|
|
53
|
+
GoogleSheetsQueryInterpreter
|
|
54
|
+
↓
|
|
55
|
+
GoogleSheetsClient
|
|
56
|
+
↓
|
|
57
|
+
Google Sheets API
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Development Principles
|
|
61
|
+
|
|
62
|
+
Changes should be made at the layer responsible for the behavior.
|
|
63
|
+
|
|
64
|
+
For example:
|
|
65
|
+
|
|
66
|
+
| Concern | Main layer |
|
|
67
|
+
| ------------------------ | ------------------------------ |
|
|
68
|
+
| Google API communication | `GoogleSheetsApiClient` |
|
|
69
|
+
| Driver behavior | `GoogleSheetsDriver` |
|
|
70
|
+
| SQL/query interpretation | `GoogleSheetsQueryInterpreter` |
|
|
71
|
+
| Query execution | `GoogleSheetsQueryRunner` |
|
|
72
|
+
| Data source registration | `GoogleSheetsDataSource` |
|
|
73
|
+
| Driver errors | `error.ts` |
|
|
74
|
+
|
|
75
|
+
Avoid implementing the same behavior in multiple layers.
|
|
76
|
+
|
|
77
|
+
### Running Tests
|
|
78
|
+
|
|
79
|
+
Run the complete test suite:
|
|
80
|
+
|
|
81
|
+
```bash id="8c4m2x"
|
|
82
|
+
bun test
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Run TypeScript type checking:
|
|
86
|
+
|
|
87
|
+
```bash id="1v7n5q"
|
|
88
|
+
bun run typecheck
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Individual test files can also be executed directly:
|
|
92
|
+
|
|
93
|
+
```bash id="6m3k9p"
|
|
94
|
+
bun test test/google-sheets/0001-client.test.ts
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Performance tests are kept separate:
|
|
98
|
+
|
|
99
|
+
```bash id="4q8x1c"
|
|
100
|
+
bun test test/google-sheets/0007-performance.test.ts
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Test Strategy
|
|
104
|
+
|
|
105
|
+
The project separates driver tests from Google API integration tests.
|
|
106
|
+
|
|
107
|
+
Driver and repository behavior should generally use `Memory`.
|
|
108
|
+
|
|
109
|
+
```ts id="9p2v6m"
|
|
110
|
+
const client = new Memory({
|
|
111
|
+
users: []
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
This keeps tests fast and deterministic.
|
|
116
|
+
|
|
117
|
+
The real `GoogleSheetsApiClient` is used for API integration tests.
|
|
118
|
+
|
|
119
|
+
### Adding a New Feature
|
|
120
|
+
|
|
121
|
+
When implementing a new feature:
|
|
122
|
+
|
|
123
|
+
1. Identify the responsible layer.
|
|
124
|
+
2. Add or update the relevant test.
|
|
125
|
+
3. Implement the smallest required production change.
|
|
126
|
+
4. Run the focused test.
|
|
127
|
+
5. Run type checking.
|
|
128
|
+
6. Run the complete test suite.
|
|
129
|
+
7. Update the documentation when the supported behavior changes.
|
|
130
|
+
|
|
131
|
+
A typical workflow is:
|
|
132
|
+
|
|
133
|
+
```bash id="2x6m8q"
|
|
134
|
+
bun test <focused-test>
|
|
135
|
+
bun run typecheck
|
|
136
|
+
bun test
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Avoid Unnecessary Changes
|
|
140
|
+
|
|
141
|
+
The driver has multiple compatibility layers with TypeORM.
|
|
142
|
+
|
|
143
|
+
A seemingly small change can affect existing repository behavior.
|
|
144
|
+
|
|
145
|
+
When a test already covers stable behavior, avoid changing its implementation unless a concrete regression or missing requirement has been identified.
|
|
146
|
+
|
|
147
|
+
Prefer targeted changes over broad refactoring.
|
|
148
|
+
|
|
149
|
+
### Adding Tests
|
|
150
|
+
|
|
151
|
+
Tests should verify behavior rather than implementation details whenever possible.
|
|
152
|
+
|
|
153
|
+
For example, repository behavior should preferably be tested through:
|
|
154
|
+
|
|
155
|
+
```ts id="5n9q3v"
|
|
156
|
+
const repository = dataSource.getRepository(User);
|
|
157
|
+
|
|
158
|
+
const users = await repository.find();
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Direct `QueryRunner` tests are appropriate when behavior cannot be reliably exercised through the repository API.
|
|
162
|
+
|
|
163
|
+
This is particularly useful for low-level driver validation such as explicit primary-key handling.
|
|
164
|
+
|
|
165
|
+
### Performance Tests
|
|
166
|
+
|
|
167
|
+
Performance tests should remain separate from functional tests.
|
|
168
|
+
|
|
169
|
+
The existing performance baseline uses `Memory` and measures operations over 1,000 rows.
|
|
170
|
+
|
|
171
|
+
Performance measurements should be treated as regression indicators rather than absolute guarantees.
|
|
172
|
+
|
|
173
|
+
### Integration Tests
|
|
174
|
+
|
|
175
|
+
Changes to the Google Sheets API client should also be validated against the real Google Sheets API where appropriate.
|
|
176
|
+
|
|
177
|
+
Use a dedicated test spreadsheet and test credentials.
|
|
178
|
+
|
|
179
|
+
Never use production spreadsheet data for automated integration tests.
|
|
180
|
+
|
|
181
|
+
### TypeScript
|
|
182
|
+
|
|
183
|
+
The project is written in TypeScript and should remain type-safe.
|
|
184
|
+
|
|
185
|
+
Before submitting changes, run:
|
|
186
|
+
|
|
187
|
+
```bash id="7c4m1x"
|
|
188
|
+
bun run typecheck
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Avoid using `any` unless the TypeORM or Google API boundary genuinely requires it.
|
|
192
|
+
|
|
193
|
+
### Debugging
|
|
194
|
+
|
|
195
|
+
When debugging driver behavior, trace the execution path from the repository toward the client:
|
|
196
|
+
|
|
197
|
+
```text id="0v8n2q"
|
|
198
|
+
Repository
|
|
199
|
+
↓
|
|
200
|
+
QueryBuilder
|
|
201
|
+
↓
|
|
202
|
+
QueryRunner
|
|
203
|
+
↓
|
|
204
|
+
Interpreter
|
|
205
|
+
↓
|
|
206
|
+
Client
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
This helps determine whether a problem originates from:
|
|
210
|
+
|
|
211
|
+
- TypeORM query generation
|
|
212
|
+
- query interpretation
|
|
213
|
+
- driver execution
|
|
214
|
+
- Google Sheets client behavior
|
|
215
|
+
- Google Sheets API behavior
|
|
216
|
+
|
|
217
|
+
### Pull Request Checklist
|
|
218
|
+
|
|
219
|
+
Before submitting a change:
|
|
220
|
+
|
|
221
|
+
```text id="3k7m1x"
|
|
222
|
+
[ ] Focused tests pass
|
|
223
|
+
[ ] TypeScript typecheck passes
|
|
224
|
+
[ ] Full test suite passes
|
|
225
|
+
[ ] Integration tests updated if necessary
|
|
226
|
+
[ ] Performance tests considered if relevant
|
|
227
|
+
[ ] Documentation updated if behavior changed
|
|
228
|
+
[ ] No credentials or sensitive data committed
|
|
229
|
+
[ ] No unrelated refactoring included
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Summary
|
|
233
|
+
|
|
234
|
+
The development process should prioritize:
|
|
235
|
+
|
|
236
|
+
- small, targeted changes
|
|
237
|
+
- behavior-driven tests
|
|
238
|
+
- clear separation between driver and API tests
|
|
239
|
+
- TypeScript type safety
|
|
240
|
+
- preserving existing compatibility
|
|
241
|
+
- documentation that reflects tested capabilities
|
|
242
|
+
|
|
243
|
+
The complete test suite should remain green before a change is considered complete.
|
package/docs/entities.md
ADDED
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
# Entity Definition
|
|
2
|
+
|
|
3
|
+
`tantan-typeorm-gs` uses TypeORM entities to define the structure of data stored in Google Sheets.
|
|
4
|
+
|
|
5
|
+
Each entity represents a worksheet, and each entity property represents a column in that worksheet.
|
|
6
|
+
|
|
7
|
+
## Basic Entity
|
|
8
|
+
|
|
9
|
+
A simple entity can be defined using TypeORM decorators:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";
|
|
13
|
+
|
|
14
|
+
@Entity("Users")
|
|
15
|
+
export class User {
|
|
16
|
+
@PrimaryGeneratedColumn()
|
|
17
|
+
id!: number;
|
|
18
|
+
|
|
19
|
+
@Column()
|
|
20
|
+
name!: string;
|
|
21
|
+
|
|
22
|
+
@Column()
|
|
23
|
+
age!: number;
|
|
24
|
+
|
|
25
|
+
@Column()
|
|
26
|
+
active!: boolean;
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The entity above is mapped to a worksheet named `Users`.
|
|
31
|
+
|
|
32
|
+
The corresponding worksheet structure is:
|
|
33
|
+
|
|
34
|
+
| id | name | age | active |
|
|
35
|
+
| --- | ---- | --- | ------ |
|
|
36
|
+
| 1 | Budi | 30 | TRUE |
|
|
37
|
+
| 2 | Siti | 25 | FALSE |
|
|
38
|
+
|
|
39
|
+
The worksheet name comes from the value passed to `@Entity()`.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Entity Name
|
|
44
|
+
|
|
45
|
+
Use `@Entity()` to define the worksheet name:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
@Entity("Users")
|
|
49
|
+
export class User {
|
|
50
|
+
// ...
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The name passed to `@Entity()` is used as the worksheet name.
|
|
55
|
+
|
|
56
|
+
If the worksheet does not exist and synchronization is enabled, the driver can create it automatically.
|
|
57
|
+
|
|
58
|
+
See [Synchronization](synchronization.md) for more information.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Columns
|
|
63
|
+
|
|
64
|
+
Use `@Column()` to define entity properties that are stored as worksheet columns:
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
@Entity("Users")
|
|
68
|
+
export class User {
|
|
69
|
+
@PrimaryGeneratedColumn()
|
|
70
|
+
id!: number;
|
|
71
|
+
|
|
72
|
+
@Column()
|
|
73
|
+
name!: string;
|
|
74
|
+
|
|
75
|
+
@Column()
|
|
76
|
+
age!: number;
|
|
77
|
+
|
|
78
|
+
@Column()
|
|
79
|
+
active!: boolean;
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The property names are mapped to worksheet columns by TypeORM metadata.
|
|
84
|
+
|
|
85
|
+
The driver supports the following commonly used types:
|
|
86
|
+
|
|
87
|
+
| Type | Status |
|
|
88
|
+
| --------- | --------- |
|
|
89
|
+
| `string` | Supported |
|
|
90
|
+
| `number` | Supported |
|
|
91
|
+
| `boolean` | Supported |
|
|
92
|
+
| `Date` | Supported |
|
|
93
|
+
| `uuid` | Supported |
|
|
94
|
+
| `int` | Supported |
|
|
95
|
+
|
|
96
|
+
Google Sheets commonly returns cell values as strings. The driver uses TypeORM column metadata to hydrate values into their expected application types.
|
|
97
|
+
|
|
98
|
+
For example, a sheet value such as:
|
|
99
|
+
|
|
100
|
+
```text
|
|
101
|
+
30
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
can be hydrated as:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
30; // number
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
when the entity property is declared as a numeric column.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Primary Columns
|
|
115
|
+
|
|
116
|
+
Entities should define a primary column.
|
|
117
|
+
|
|
118
|
+
### Generated Primary Key
|
|
119
|
+
|
|
120
|
+
Use `@PrimaryGeneratedColumn()` for automatically generated numeric IDs:
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
@Entity("Users")
|
|
124
|
+
export class User {
|
|
125
|
+
@PrimaryGeneratedColumn()
|
|
126
|
+
id!: number;
|
|
127
|
+
|
|
128
|
+
@Column()
|
|
129
|
+
name!: string;
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The driver supports generated numeric IDs and UUIDs.
|
|
134
|
+
|
|
135
|
+
### UUID Primary Key
|
|
136
|
+
|
|
137
|
+
A UUID can be used as a generated primary key:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
@Entity("Users")
|
|
141
|
+
export class User {
|
|
142
|
+
@PrimaryGeneratedColumn("uuid")
|
|
143
|
+
id!: string;
|
|
144
|
+
|
|
145
|
+
@Column()
|
|
146
|
+
name!: string;
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Explicit Primary Key
|
|
151
|
+
|
|
152
|
+
Use `@PrimaryColumn()` when the application provides the primary key:
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
@Entity("Users")
|
|
156
|
+
export class User {
|
|
157
|
+
@PrimaryColumn()
|
|
158
|
+
id!: string;
|
|
159
|
+
|
|
160
|
+
@Column()
|
|
161
|
+
name!: string;
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Primary key values must be unique within the worksheet.
|
|
166
|
+
|
|
167
|
+
The driver does not provide database-level primary key enforcement like a relational database. Primary key validation is performed by the driver during supported operations.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Column Options
|
|
172
|
+
|
|
173
|
+
Standard TypeORM column options can be used where supported by the driver.
|
|
174
|
+
|
|
175
|
+
For example:
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
@Column({
|
|
179
|
+
nullable: true,
|
|
180
|
+
})
|
|
181
|
+
description!: string | null;
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Default values are also supported:
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
@Column({
|
|
188
|
+
default: true,
|
|
189
|
+
})
|
|
190
|
+
active!: boolean;
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The driver applies the configured default when inserting entities where the value is not explicitly provided.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Custom Column Names
|
|
198
|
+
|
|
199
|
+
TypeORM column names can be customized using the `name` option:
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
@Entity("Users")
|
|
203
|
+
export class User {
|
|
204
|
+
@PrimaryGeneratedColumn()
|
|
205
|
+
id!: number;
|
|
206
|
+
|
|
207
|
+
@Column({
|
|
208
|
+
name: "full_name"
|
|
209
|
+
})
|
|
210
|
+
name!: string;
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
The worksheet will use:
|
|
215
|
+
|
|
216
|
+
| id | full_name |
|
|
217
|
+
| --- | --------- |
|
|
218
|
+
| 1 | Budi |
|
|
219
|
+
|
|
220
|
+
The entity property remains `name`:
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
user.name;
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
while the worksheet column is:
|
|
227
|
+
|
|
228
|
+
```text
|
|
229
|
+
full_name
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Column names are resolved through TypeORM's entity metadata.
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Date Columns
|
|
237
|
+
|
|
238
|
+
`Date` columns are supported:
|
|
239
|
+
|
|
240
|
+
```ts
|
|
241
|
+
@Entity("Users")
|
|
242
|
+
export class User {
|
|
243
|
+
@PrimaryGeneratedColumn()
|
|
244
|
+
id!: number;
|
|
245
|
+
|
|
246
|
+
@Column()
|
|
247
|
+
name!: string;
|
|
248
|
+
|
|
249
|
+
@Column()
|
|
250
|
+
createdAt!: Date;
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
When reading data, the driver hydrates the worksheet value into a JavaScript `Date` according to the column metadata.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Soft Delete
|
|
259
|
+
|
|
260
|
+
Soft-delete entities can use TypeORM's `@DeleteDateColumn()`:
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
import { Entity, PrimaryGeneratedColumn, Column, DeleteDateColumn } from "typeorm";
|
|
264
|
+
|
|
265
|
+
@Entity("Users")
|
|
266
|
+
export class User {
|
|
267
|
+
@PrimaryGeneratedColumn()
|
|
268
|
+
id!: number;
|
|
269
|
+
|
|
270
|
+
@Column()
|
|
271
|
+
name!: string;
|
|
272
|
+
|
|
273
|
+
@DeleteDateColumn({
|
|
274
|
+
nullable: true
|
|
275
|
+
})
|
|
276
|
+
deletedAt!: Date | null;
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
The driver supports:
|
|
281
|
+
|
|
282
|
+
- `@DeleteDateColumn()`
|
|
283
|
+
- `softDelete()`
|
|
284
|
+
- `softRemove()`
|
|
285
|
+
- `restore()`
|
|
286
|
+
- Excluding soft-deleted rows from normal queries
|
|
287
|
+
- Including soft-deleted rows with `withDeleted`
|
|
288
|
+
|
|
289
|
+
For example:
|
|
290
|
+
|
|
291
|
+
```ts
|
|
292
|
+
await userRepository.softDelete(user.id);
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Normal queries will exclude the soft-deleted entity:
|
|
296
|
+
|
|
297
|
+
```ts
|
|
298
|
+
const users = await userRepository.find();
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
To include soft-deleted entities:
|
|
302
|
+
|
|
303
|
+
```ts
|
|
304
|
+
const users = await userRepository.find({
|
|
305
|
+
withDeleted: true
|
|
306
|
+
});
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
See [CRUD Operations](crud.md) for more information.
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## Relations
|
|
314
|
+
|
|
315
|
+
TypeORM relation metadata can be used with Google Sheets.
|
|
316
|
+
|
|
317
|
+
For example:
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
@Entity("Users")
|
|
321
|
+
export class User {
|
|
322
|
+
@PrimaryGeneratedColumn()
|
|
323
|
+
id!: number;
|
|
324
|
+
|
|
325
|
+
@Column()
|
|
326
|
+
name!: string;
|
|
327
|
+
|
|
328
|
+
@OneToMany(() => Post, (post) => post.user)
|
|
329
|
+
posts!: Post[];
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
@Entity("Posts")
|
|
333
|
+
export class Post {
|
|
334
|
+
@PrimaryGeneratedColumn()
|
|
335
|
+
id!: number;
|
|
336
|
+
|
|
337
|
+
@Column()
|
|
338
|
+
title!: string;
|
|
339
|
+
|
|
340
|
+
@Column()
|
|
341
|
+
userId!: number;
|
|
342
|
+
|
|
343
|
+
@ManyToOne(() => User, (user) => user.posts)
|
|
344
|
+
@JoinColumn({
|
|
345
|
+
name: "userId"
|
|
346
|
+
})
|
|
347
|
+
user!: User;
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
The `Posts` worksheet stores the foreign-key value in the `userId` column.
|
|
352
|
+
|
|
353
|
+
An explicit foreign-key value can be persisted normally:
|
|
354
|
+
|
|
355
|
+
```ts
|
|
356
|
+
post.userId = user.id;
|
|
357
|
+
|
|
358
|
+
await postRepository.save(post);
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
Relations can also be loaded explicitly:
|
|
362
|
+
|
|
363
|
+
```ts
|
|
364
|
+
const users = await userRepository.find({
|
|
365
|
+
relations: {
|
|
366
|
+
posts: true
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Supported relation features include:
|
|
372
|
+
|
|
373
|
+
- `@ManyToOne`
|
|
374
|
+
- `@OneToMany`
|
|
375
|
+
- `@JoinColumn`
|
|
376
|
+
- Explicit foreign-key column persistence
|
|
377
|
+
- Explicit relation loading
|
|
378
|
+
- Relation queries through supported joins
|
|
379
|
+
- Soft-delete filtering for relations
|
|
380
|
+
- `withDeleted` relation loading
|
|
381
|
+
|
|
382
|
+
Nested relation persistence and cascade persistence are not fully supported.
|
|
383
|
+
|
|
384
|
+
For example, applications should not rely on automatically persisting a nested relation through:
|
|
385
|
+
|
|
386
|
+
```ts
|
|
387
|
+
await userRepository.save({
|
|
388
|
+
name: "Budi",
|
|
389
|
+
posts: [
|
|
390
|
+
{
|
|
391
|
+
title: "Post 1"
|
|
392
|
+
}
|
|
393
|
+
]
|
|
394
|
+
});
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
Applications should explicitly persist related entities when necessary.
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## Naming Strategies
|
|
402
|
+
|
|
403
|
+
Entity and column names are resolved through TypeORM metadata.
|
|
404
|
+
|
|
405
|
+
The driver does not implement a separate naming-strategy system. TypeORM-generated metadata is used to determine the worksheet and column names.
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
## Entity Metadata
|
|
410
|
+
|
|
411
|
+
Entity definitions are processed by TypeORM before the Google Sheets driver handles them.
|
|
412
|
+
|
|
413
|
+
The driver consumes TypeORM's `EntityMetadata`, including information such as:
|
|
414
|
+
|
|
415
|
+
- Entity name
|
|
416
|
+
- Worksheet name
|
|
417
|
+
- Column names
|
|
418
|
+
- Column types
|
|
419
|
+
- Primary columns
|
|
420
|
+
- Generated columns
|
|
421
|
+
- Delete-date columns
|
|
422
|
+
- Relations
|
|
423
|
+
- Join columns
|
|
424
|
+
- Nullable columns
|
|
425
|
+
- Default values
|
|
426
|
+
|
|
427
|
+
This means entity definitions remain standard TypeORM entity definitions rather than using a Google Sheets-specific schema format.
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
## Example
|
|
432
|
+
|
|
433
|
+
A complete entity setup might look like:
|
|
434
|
+
|
|
435
|
+
```ts
|
|
436
|
+
import { Column, DeleteDateColumn, Entity, JoinColumn, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm";
|
|
437
|
+
|
|
438
|
+
@Entity("Users")
|
|
439
|
+
export class User {
|
|
440
|
+
@PrimaryGeneratedColumn()
|
|
441
|
+
id!: number;
|
|
442
|
+
|
|
443
|
+
@Column()
|
|
444
|
+
name!: string;
|
|
445
|
+
|
|
446
|
+
@Column()
|
|
447
|
+
active!: boolean;
|
|
448
|
+
|
|
449
|
+
@DeleteDateColumn({
|
|
450
|
+
nullable: true
|
|
451
|
+
})
|
|
452
|
+
deletedAt!: Date | null;
|
|
453
|
+
|
|
454
|
+
@OneToMany(() => Post, (post) => post.user)
|
|
455
|
+
posts!: Post[];
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
@Entity("Posts")
|
|
459
|
+
export class Post {
|
|
460
|
+
@PrimaryGeneratedColumn()
|
|
461
|
+
id!: number;
|
|
462
|
+
|
|
463
|
+
@Column()
|
|
464
|
+
title!: string;
|
|
465
|
+
|
|
466
|
+
@Column()
|
|
467
|
+
userId!: number;
|
|
468
|
+
|
|
469
|
+
@DeleteDateColumn({
|
|
470
|
+
nullable: true
|
|
471
|
+
})
|
|
472
|
+
deletedAt!: Date | null;
|
|
473
|
+
|
|
474
|
+
@ManyToOne(() => User, (user) => user.posts)
|
|
475
|
+
@JoinColumn({
|
|
476
|
+
name: "userId"
|
|
477
|
+
})
|
|
478
|
+
user!: User;
|
|
479
|
+
}
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
This definition can be used directly with a TypeORM `DataSource` configured for the Google Sheets driver.
|
|
483
|
+
|
|
484
|
+
## Limitations
|
|
485
|
+
|
|
486
|
+
Entity definitions use TypeORM metadata, but not every TypeORM feature can be represented by Google Sheets.
|
|
487
|
+
|
|
488
|
+
In particular:
|
|
489
|
+
|
|
490
|
+
- Transactions are not supported.
|
|
491
|
+
- Foreign-key constraints are not enforced by Google Sheets.
|
|
492
|
+
- Database-level cascade operations are not available.
|
|
493
|
+
- Nested relation persistence and full cascade persistence are not fully supported.
|
|
494
|
+
- Lazy relation loading is not supported.
|
|
495
|
+
- Relational database guarantees such as referential integrity are not provided.
|
|
496
|
+
|
|
497
|
+
See [Supported Features](supported-features.md) and [Limitations](limitations.md) for the complete compatibility details.
|
|
498
|
+
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
Ini sudah saya buat sebagai **`docs/entities.md`** dan sengaja tidak memasukkan detail CRUD terlalu jauh supaya dokumentasinya tidak tumpang tindih dengan `crud.md`.
|
|
502
|
+
```
|