1
0
mirror of synced 2025-12-19 18:14:56 -05:00

chore: rework docs numbering (#70295)

This commit is contained in:
Jimmy Ma
2025-12-02 10:55:45 -08:00
committed by GitHub
parent eb7433b298
commit 5fb70f1b9f
7 changed files with 930 additions and 586 deletions

View File

@@ -22,28 +22,28 @@
**Timeline:** 2-3 days
**Steps:**
1. **1-getting-started.md** (Phases 0-1, ~4 hours)
1. **1-getting-started.md** (Setup Phases 1-2, ~4 hours)
- Project scaffolding and build setup
- Spec operation implementation
- **Milestone:** `./destination-{db} --spec` works
2. **2-database-setup.md** (Phases 2-5, ~6 hours)
- Database connectivity and basic operations
- Namespace and table operations
2. **2-database-setup.md** (Database Phases 1-2, ~6 hours)
- Database connectivity and all table operations
- Check operation implementation
- **Milestone:** `./destination-{db} --check --config config.json` works
3. **3-write-infrastructure.md** (Phases 6-7, ~4 hours)
3. **3-write-infrastructure.md** (Infrastructure Phases 1-2, ~4 hours)
- Name generators and DI setup
- Write operation infrastructure
- Understanding test contexts
- **Milestone:** DI configured, ready for business logic
4. **4-write-operations.md** (Phases 8-11, ~8 hours)
4. **4-write-operations.md** (Write Phases 1-4, ~8 hours)
- InsertBuffer, Aggregate, Writer implementation
- Append mode (direct writes)
- Generation ID support
- Overwrite mode (atomic swap)
- Copy operation
- **Milestone:** `./destination-{db} --write` works with append + overwrite modes
**Result:** Working connector suitable for PoC and simple use cases
@@ -59,14 +59,14 @@
**Steps:**
1-4. Complete Fast Path (above)
5. **5-advanced-features.md** (Phases 12-15, ~12 hours)
5. **5-advanced-features.md** (Advanced Phases 1-4, ~12 hours)
- Schema evolution (automatic column add/drop/modify)
- Dedupe mode (MERGE with primary key)
- CDC support (hard/soft deletes)
- Optimization and polish
- **Milestone:** Full-featured, production-ready connector
6. **6-testing.md** (~2 hours)
6. **6-testing.md** (Testing Phase 1, ~2 hours)
- Run BasicFunctionalityIntegrationTest
- Validate all sync modes
- Test schema evolution and CDC
@@ -98,12 +98,12 @@
| Guide | Phases | What Works | Lines | Time | Prerequisites |
|-------|--------|------------|-------|------|---------------|
| **1-getting-started.md** | 0-1 | --spec | ~626 | 4h | None |
| **2-database-setup.md** | 2-5 | --check | ~902 | 6h | Guide 1 |
| **3-write-infrastructure.md** | 6-7 | DI ready | ~855 | 4h | Guide 2 |
| **4-write-operations.md** | 8-11 | --write (append, overwrite) | ~869 | 8h | Guide 3 |
| **5-advanced-features.md** | 12-15 | All features | ~1020 | 12h | Guide 4 |
| **6-testing.md** | Tests | All tests pass | ~878 | 2h | Guide 5 |
| **1-getting-started.md** | Setup 1-2 | --spec | ~626 | 4h | None |
| **2-database-setup.md** | Database 1-2 | --check | ~1180 | 6h | Guide 1 |
| **3-write-infrastructure.md** | Infrastructure 1-2 | DI ready | ~600 | 4h | Guide 2 |
| **4-write-operations.md** | Write 1-4 | --write (append, overwrite) | ~780 | 8h | Guide 3 |
| **5-advanced-features.md** | Advanced 1-4 | All features | ~900 | 12h | Guide 4 |
| **6-testing.md** | Testing 1 | All tests pass | ~730 | 2h | Guide 5 |
| **7-troubleshooting.md** | Reference | Debug help | ~280 | As needed | Any |
---
@@ -234,7 +234,7 @@ Platform → stdin → Lifecycle → Writer.setup()
- Look at destination-snowflake or destination-clickhouse for examples
**Common pitfalls:**
- Not reading test contexts section (causes confusion in Phase 7)
- Not reading test contexts section (causes confusion in Infrastructure Phase 2)
- Missing DI registration (causes "No bean found" errors)
- Skipping CDK version pinning (causes build issues)
- Not understanding StreamLoader variants (causes wrong finalization)

View File

@@ -11,13 +11,13 @@
---
## Phase 0: Scaffolding
## Setup Phase 1: Scaffolding
**Goal:** Empty project structure that builds
**Checkpoint:** Project compiles
### Step 0.1: Create Directory Structure
### Setup Step 1: Create Directory Structure
```bash
cd airbyte-integrations/connectors
@@ -34,7 +34,7 @@ mkdir check client config dataflow spec write
mkdir write/load write/transform
```
### Step 0.2: Create gradle.properties with CDK Version Pin
### Setup Step 2: Create gradle.properties with CDK Version Pin
**File:** `destination-{db}/gradle.properties`
@@ -56,7 +56,7 @@ cdkVersion=0.1.76
./gradlew destination-{db}:upgradeCdk --cdkVersion=0.1.76
```
### Step 0.3: Create build.gradle.kts
### Setup Step 3: Create build.gradle.kts
**File:** `destination-{db}/build.gradle.kts`
@@ -88,7 +88,7 @@ dependencies {
**No need to manually declare CDK dependencies** - the plugin handles it
### Step 0.4: Create metadata.yaml
### Setup Step 4: Create metadata.yaml
**File:** `destination-{db}/metadata.yaml`
@@ -145,7 +145,7 @@ metadataSpecVersion: "1.0"
grep "baseImage:" airbyte-integrations/connectors/destination-*/metadata.yaml | sort | uniq -c | sort -rn | head -3
```
### Step 0.5: Configure Docker Build in build.gradle.kts
### Setup Step 5: Configure Docker Build in build.gradle.kts
**File:** Update `destination-{db}/build.gradle.kts`
@@ -181,7 +181,7 @@ dependencies {
- `io.airbyte.gradle.docker`: Provides Docker build tasks
- `airbyte-connector-docker-convention`: Reads metadata.yaml, generates build args
### Step 0.6: Create Main Entry Point
### Setup Step 6: Create Main Entry Point
**File:** `destination-{db}/src/main/kotlin/.../{DB}Destination.kt`
@@ -197,7 +197,7 @@ fun main(args: Array<String>) {
**That's it!** The framework handles everything else.
### Step 0.7: Verify Build
### Setup Step 7: Verify Build
```bash
$ ./gradlew :destination-{db}:build
@@ -211,7 +211,7 @@ $ ./gradlew :destination-{db}:build
- Micronaut scanning issues? Ensure `@Singleton` annotations present
- metadata.yaml syntax errors? Validate YAML format
### Step 0.8: Create application-connector.yml
### Setup Step 8: Create application-connector.yml
**File:** `src/main/resources/application-connector.yml`
@@ -257,7 +257,7 @@ Failed to inject value for parameter [fileTransferEnabled]
-`mappers.namespace-mapping-config-path`: Namespace mapping file path (empty for identity)
-`file-transfer.enabled`: Whether connector transfers files (false for databases)
### Step 0.9: Build Docker Image
### Setup Step 9: Build Docker Image
```bash
$ ./gradlew :destination-{db}:assemble
@@ -291,13 +291,13 @@ airbyte/destination-{db} 0.1.0 abc123def456 2 minutes ago 500MB
---
## Phase 1: Spec Operation
## Setup Phase 2: Spec Operation
**Goal:** Implement --spec operation (returns connector configuration schema)
**Checkpoint:** Spec test passes
### Step 1.1: Understand Configuration Classes
### Setup Step 1: Understand Configuration Classes
**Two classes work together for configuration:**
@@ -317,7 +317,7 @@ ConfigurationFactory parses JSON → Configuration object
Your code uses Configuration object
```
### Step 1.2: Create Specification Class
### Setup Step 2: Create Specification Class
**Purpose:** Defines the configuration form users fill in Airbyte UI
@@ -361,7 +361,7 @@ open class {DB}Specification : ConfigurationSpecification() {
- `@JsonSchemaTitle("Title")` - Label in UI (optional, defaults to property name)
- `@JsonSchemaInject(json = """{"airbyte_secret": true}""")` - Mark as secret (passwords, API keys)
### Step 1.3: Create Configuration and Factory
### Setup Step 3: Create Configuration and Factory
**Purpose:** Runtime configuration object your code actually uses
@@ -411,7 +411,7 @@ class {DB}ConfigurationFactory :
- Specification = What users configure
- Configuration = What your code uses
### Step 1.4: Create Specification Extension
### Setup Step 4: Create Specification Extension
**Purpose:** Declares what sync modes your connector supports
@@ -443,7 +443,7 @@ class {DB}SpecificationExtension : DestinationSpecificationExtension {
}
```
### Step 1.5: Configure Documentation URL
### Setup Step 5: Configure Documentation URL
**File:** `src/main/resources/application.yml`
@@ -472,7 +472,7 @@ airbyteBulkConnector {
**Default:** If not specified, uses placeholder URL
### Step 1.6: Create Expected Spec Test File
### Setup Step 6: Create Expected Spec Test File
**File:** `src/test-integration/resources/expected-spec-oss.json`
@@ -539,7 +539,7 @@ airbyteBulkConnector {
2. Copying output to this file
3. Using it for regression testing
### Step 1.7: Create Spec Test
### Setup Step 7: Create Spec Test
**File:** `src/test-integration/kotlin/.../spec/{DB}SpecTest.kt`
@@ -557,7 +557,7 @@ class {DB}SpecTest : SpecTest()
- Matches expected-spec-oss.json (snapshot test)
- If Cloud-specific: Matches expected-spec-cloud.json
### Step 1.8: Generate and Validate Spec
### Setup Step 8: Generate and Validate Spec
**Run spec operation to generate the JSON schema:**
@@ -590,7 +590,7 @@ mkdir -p src/test-integration/resources
**Tip:** Use `jq` to format: `./gradlew :destination-{db}:run --args='--spec' | jq .spec > expected-spec-oss.json`
### Step 1.9: Run Spec Test
### Setup Step 9: Run Spec Test
```bash
$ ./gradlew :destination-{db}:integrationTestSpecOss

File diff suppressed because it is too large Load Diff

View File

@@ -12,7 +12,7 @@ After completing this guide, you'll have:
---
## Phase 6: Name Generators & TableCatalog DI
## Infrastructure Phase 1: Name Generators & TableCatalog DI
**Goal:** Create name generator beans required for TableCatalog instantiation
@@ -25,7 +25,7 @@ After completing this guide, you'll have:
Without these beans, you'll get **"Error instantiating TableCatalog"** or **"No bean of type [FinalTableNameGenerator]"** errors in Phase 7 write tests.
### Step 6.1: Create RawTableNameGenerator
### Infrastructure Step 1: Create RawTableNameGenerator
**File:** `config/{DB}NameGenerators.kt`
@@ -58,7 +58,7 @@ class {DB}RawTableNameGenerator(
- Modern connectors typically use final tables only, but interface must be implemented
- Keep implementation simple (identity mapping is fine)
### Step 6.2: Create FinalTableNameGenerator
### Infrastructure Step 2: Create FinalTableNameGenerator
**Add to same file:** `config/{DB}NameGenerators.kt`
@@ -90,7 +90,7 @@ class {DB}FinalTableNameGenerator(
// Output: TableName("my_database", "customers") // Uses config.database as fallback
```
### Step 6.3: Create ColumnNameGenerator
### Infrastructure Step 3: Create ColumnNameGenerator
**Add to same file:** `config/{DB}NameGenerators.kt`
@@ -123,7 +123,7 @@ class {DB}ColumnNameGenerator : ColumnNameGenerator {
"userId" "userId"
```
### Step 6.4: Add Name Transformation Helper
### Infrastructure Step 4: Add Name Transformation Helper
**Add to same file:** `config/{DB}NameGenerators.kt`
@@ -177,7 +177,7 @@ private fun String.toDbCompatible(): String {
private val POSTGRES_RESERVED_WORDS = setOf("user", "table", "select", ...)
```
### Step 6.5: Register TempTableNameGenerator in BeanFactory
### Infrastructure Step 5: Register TempTableNameGenerator in BeanFactory
**File:** Update `{DB}BeanFactory.kt`
@@ -200,7 +200,7 @@ fun tempTableNameGenerator(config: {DB}Configuration): TempTableNameGenerator {
- CDK provides implementation, but YOU must register it
- Used by Writer to create staging tables
### Step 6.6: Verify Compilation
### Infrastructure Step 6: Verify Compilation
**Validate:**
```bash
@@ -227,7 +227,7 @@ $ ./gradlew :destination-{db}:integrationTest # testSpecOss, testSuccessConfigs
---
## Phase 7: Write Operation Infrastructure
## Infrastructure Phase 2: Write Operation Infrastructure
**Goal:** Create write operation infrastructure beans (no business logic yet)
@@ -242,7 +242,7 @@ $ ./gradlew :destination-{db}:integrationTest # testSpecOss, testSuccessConfigs
**Key insight:** Separate infrastructure DI from business logic DI to catch errors incrementally.
### Step 7.1: Create WriteOperationV2
### Infrastructure Step 1: Create WriteOperationV2
**File:** `cdk/WriteOperationV2.kt`
@@ -293,7 +293,7 @@ IllegalStateException: A legal sync requires a declared @Singleton of a type tha
- Many connectors keep this file identical across databases
- Signals "this is infrastructure, not business logic"
### Step 7.2: Create DatabaseInitialStatusGatherer
### Infrastructure Step 2: Create DatabaseInitialStatusGatherer
**File:** `config/{DB}DirectLoadDatabaseInitialStatusGatherer.kt`
@@ -337,7 +337,7 @@ DirectLoadInitialStatus(
⚠️ **MISSING IN V1 GUIDE:** This step existed as code but bean registration was missing!
### Step 7.3: Register DatabaseInitialStatusGatherer in BeanFactory
### Infrastructure Step 3: Register DatabaseInitialStatusGatherer in BeanFactory
**File:** Update `{DB}BeanFactory.kt`
@@ -363,7 +363,7 @@ fun initialStatusGatherer(
- Micronaut needs explicit return type for generic beans
- Factory method provides type safety
### Step 7.4: Create ColumnNameMapper
### Infrastructure Step 4: Create ColumnNameMapper
**File:** `write/transform/{DB}ColumnNameMapper.kt`
@@ -407,7 +407,7 @@ class {DB}ColumnNameMapper(
- ColumnNameMapper: Uses mappings during transform (Phase 7)
- Separation of concerns: generation vs. application
### Step 7.5: Register AggregatePublishingConfig in BeanFactory
### Infrastructure Step 5: Register AggregatePublishingConfig in BeanFactory
**File:** Update `{DB}BeanFactory.kt`
@@ -449,7 +449,7 @@ fun aggregatePublishingConfig(dataChannelMedium: DataChannelMedium): AggregatePu
- Tune later based on performance requirements
- Start with defaults - they work for most databases
### Step 7.6: Create WriteInitializationTest
### Infrastructure Step 6: Create WriteInitializationTest
**File:** `src/test-integration/kotlin/.../write/{DB}WriteInitTest.kt`
@@ -498,7 +498,7 @@ Phase 7: WriteInitTest validates they work with real catalog
Phase 8: ConnectorWiringSuite validates full write path with mock catalog
```
### Step 7.6: Create Test Config File
### Infrastructure Step 7: Create Test Config File
**File:** `secrets/config.json`
@@ -525,7 +525,7 @@ $ mkdir -p destination-{db}/secrets
**Note:** Add `secrets/` to `.gitignore` to avoid committing credentials
### Step 7.7: Validate WriteInitializationTest
### Infrastructure Step 8: Validate WriteInitializationTest
**Validate:**
```bash

View File

@@ -13,7 +13,7 @@
---
## Phase 8: Writer & Append Mode (Business Logic)
## Write Phase 1: Writer & Append Mode (Business Logic)
**Goal:** Implement actual data writing (Writer, Aggregate, InsertBuffer)
@@ -28,7 +28,7 @@
**Key insight:** Infrastructure DI (Phase 7) is separate from business logic DI (Phase 8).
Phase 7 validates "can we start?" Phase 8 validates "can we write data?"
### Step 8.1: Create InsertBuffer
### Write Step 1: Create InsertBuffer
**File:** `write/load/{DB}InsertBuffer.kt`
@@ -109,7 +109,7 @@ class {DB}InsertBuffer(
- Buffers hold stream-specific state (table name, accumulated records)
- AggregateFactory creates one buffer per stream
### Step 8.2: Add executeInsert() to Client
### Write Step 2: Add executeInsert() to Client
**File:** Update `client/{DB}AirbyteClient.kt`
@@ -145,7 +145,7 @@ private fun setParameter(statement: PreparedStatement, index: Int, value: Airbyt
**Note:** For non-JDBC databases, use native client APIs (e.g., MongoDB insertOne, ClickHouse native client)
### Step 8.3: Create Aggregate
### Write Step 3: Create Aggregate
**File:** `dataflow/{DB}Aggregate.kt`
@@ -199,7 +199,7 @@ InsertBuffer.accumulate()
Database
```
### Step 8.4: Create AggregateFactory
### Write Step 4: Create AggregateFactory
**File:** `dataflow/{DB}AggregateFactory.kt`
@@ -249,7 +249,7 @@ class {DB}AggregateFactory(
- Can't use constructor injection (dynamic stream list)
- Factory receives StoreKey, looks up stream config, creates Aggregate
### Step 8.5: Create Writer
### Write Step 5: Create Writer
**File:** `write/{DB}Writer.kt`
@@ -352,7 +352,7 @@ class {DB}Writer(
- DirectLoadTableDedupStreamLoader
- DirectLoadTableDedupTruncateStreamLoader
### Step 8.6: Create ConnectorWiringSuite Test
### Write Step 6: Create ConnectorWiringSuite Test
**File:** `src/test-integration/kotlin/.../component/{DB}WiringTest.kt`
@@ -433,7 +433,7 @@ class {DB}WiringTest(
- Creates dynamic test streams
- Focuses on write logic, not catalog parsing
### Step 8.7: Validate ConnectorWiringSuite
### Write Step 7: Validate ConnectorWiringSuite
**Validate:**
```bash
@@ -471,7 +471,7 @@ $ ./gradlew :destination-{db}:integrationTest # 3 tests should pass
---
## Phase 9: Generation ID Support
## Write Phase 2: Generation ID Support
**Goal:** Track sync generations for refresh handling
@@ -486,7 +486,7 @@ $ ./gradlew :destination-{db}:integrationTest # 3 tests should pass
- Full refresh: minimumGenerationId = generationId (replace all data)
- Incremental: minimumGenerationId = 0 (keep all data)
### Step 9.1: Enable Generation ID Test
### Write Step 1: Enable Generation ID Test
**File:** Update `src/test-integration/kotlin/.../component/{DB}TableOperationsTest.kt`
@@ -502,7 +502,7 @@ override fun `get generation id`() {
- Returns 0L for tables without generation ID
- Returns actual generation ID from `_airbyte_generation_id` column
### Step 9.2: Validate
### Write Step 2: Validate
**Validate:**
```bash
@@ -516,7 +516,7 @@ $ ./gradlew :destination-{db}:componentTest # 10 tests should pass
---
## Phase 10: Overwrite Mode
## Write Phase 3: Overwrite Mode
**Goal:** Support full refresh (replace all data)
@@ -531,7 +531,7 @@ $ ./gradlew :destination-{db}:componentTest # 10 tests should pass
- **Append** (Phase 8): INSERT into existing table
- **Overwrite** (Phase 10): SWAP temp table with final table
### Step 10.1: Implement overwriteTable() in SQL Generator
### Write Step 1: Implement overwriteTable() in SQL Generator
**File:** Update `client/{DB}SqlGenerator.kt`
@@ -571,7 +571,7 @@ fun overwriteTable(source: TableName, target: TableName): List<String> {
- **Postgres/MySQL**: DROP + RENAME requires transaction for atomicity
- **BigQuery**: CREATE OR REPLACE TABLE (different pattern)
### Step 10.2: Implement overwriteTable() in Client
### Write Step 2: Implement overwriteTable() in Client
**File:** Update `client/{DB}AirbyteClient.kt`
@@ -585,7 +585,7 @@ override suspend fun overwriteTable(
}
```
### Step 10.3: Update Writer for Truncate Mode
### Write Step 3: Update Writer for Truncate Mode
**File:** Update `write/{DB}Writer.kt`
@@ -637,7 +637,7 @@ override fun createStreamLoader(stream: DestinationStream): StreamLoader {
- **AppendStreamLoader**: Writes directly to final table
- **AppendTruncateStreamLoader**: Writes to temp table, then swaps
### Step 10.4: Enable Tests
### Write Step 4: Enable Tests
**File:** Update `src/test-integration/kotlin/.../component/{DB}TableOperationsTest.kt`
@@ -648,7 +648,7 @@ override fun `overwrite tables`() {
}
```
### Step 10.5: Validate
### Write Step 5: Validate
**Validate:**
```bash
@@ -663,7 +663,7 @@ $ ./gradlew :destination-{db}:integrationTest # 3 tests should pass
---
## Phase 11: Copy Operation
## Write Phase 4: Copy Operation
**Goal:** Support table copying (used internally by some modes)
@@ -674,7 +674,7 @@ $ ./gradlew :destination-{db}:integrationTest # 3 tests should pass
- Some overwrite implementations: Copy instead of swap
- Schema evolution: Copy to new schema
### Step 11.1: Implement copyTable() in SQL Generator
### Write Step 1: Implement copyTable() in SQL Generator
**File:** Update `client/{DB}SqlGenerator.kt`
@@ -724,7 +724,7 @@ fun copyTable(
}
```
### Step 11.2: Implement copyTable() in Client
### Write Step 2: Implement copyTable() in Client
**File:** Update `client/{DB}AirbyteClient.kt`
@@ -738,7 +738,7 @@ override suspend fun copyTable(
}
```
### Step 11.3: Enable Test
### Write Step 3: Enable Test
**File:** Update `src/test-integration/kotlin/.../component/{DB}TableOperationsTest.kt`
@@ -749,7 +749,7 @@ override fun `copy tables`() {
}
```
### Step 11.4: Validate
### Write Step 4: Validate
**Validate:**
```bash

View File

@@ -22,6 +22,7 @@ After completing this guide, you'll have a production-ready connector with:
---
## Advanced Phase 1: Schema Evolution
**Goal:** Automatically adapt to schema changes
@@ -33,7 +34,7 @@ After completing this guide, you'll have a production-ready connector with:
3. **Change type**: Source changes field type → alter column (with casting)
4. **Change nullability**: Source changes nullable → alter column constraints
### Step 12.1: Implement discoverSchema()
### Advanced Step 1: Implement discoverSchema()
**File:** Update `client/{DB}AirbyteClient.kt`
@@ -90,7 +91,7 @@ private val AIRBYTE_META_COLUMNS = setOf(
- **ClickHouse**: `system.columns` or client API `getTableSchema()`
- **BigQuery**: `INFORMATION_SCHEMA.COLUMNS`
### Step 12.2: Implement computeSchema()
### Advanced Step 2: Implement computeSchema()
**File:** Update `client/{DB}AirbyteClient.kt`
@@ -117,7 +118,7 @@ override fun computeSchema(
- Applies column name mapping (Phase 6 generators)
- Uses ColumnUtils.toDialectType() from Phase 4
### Step 12.3: Implement alterTable() - ADD COLUMN
### Advanced Step 3: Implement alterTable() - ADD COLUMN
**File:** Update `client/{DB}SqlGenerator.kt`
@@ -154,7 +155,7 @@ fun alterTable(
}
```
### Step 12.4: Implement alterTable() - MODIFY COLUMN
### Advanced Step 4: Implement alterTable() - MODIFY COLUMN
**Add to alterTable():**
@@ -245,7 +246,7 @@ fun recreateTable(
}
```
### Step 12.5: Implement applyChangeset()
### Advanced Step 5: Implement applyChangeset()
**File:** Update `client/{DB}AirbyteClient.kt`
@@ -275,7 +276,7 @@ override suspend fun applyChangeset(
}
```
### Step 12.6: Implement ensureSchemaMatches()
### Advanced Step 6: Implement ensureSchemaMatches()
**File:** Update `client/{DB}AirbyteClient.kt`
@@ -303,7 +304,7 @@ override suspend fun ensureSchemaMatches(
- If source schema changed since last sync, applies schema changes
- Automatic - no user intervention needed
### Step 12.7: Validate Schema Evolution
### Advanced Step 7: Validate Schema Evolution
**Validate:**
```bash
@@ -317,7 +318,7 @@ $ ./gradlew :destination-{db}:integrationTest # 3 tests should pass
---
## Phase 13: Dedupe Mode
## Advanced Phase 2: Dedupe Mode
**Goal:** Support primary key deduplication
@@ -336,7 +337,7 @@ $ ./gradlew :destination-{db}:integrationTest # 3 tests should pass
- **Overwrite** (Phase 10): Swap tables
- **Dedupe** (Phase 13): Upsert with primary key
### Step 13.1: Implement upsertTable() in SQL Generator
### Advanced Step 1: Implement upsertTable() in SQL Generator
**Option A: MERGE Statement (Snowflake, SQL Server, BigQuery)**
@@ -500,7 +501,7 @@ fun upsertTable(...): List<String> {
}
```
### Step 13.2: Implement upsertTable() in Client
### Advanced Step 2: Implement upsertTable() in Client
**File:** Update `client/{DB}AirbyteClient.kt`
@@ -523,7 +524,7 @@ override suspend fun upsertTable(
}
```
### Step 13.3: Update Writer for Dedupe Mode
### Advanced Step 3: Update Writer for Dedupe Mode
**File:** Update `write/{DB}Writer.kt`
@@ -585,7 +586,7 @@ override fun createStreamLoader(stream: DestinationStream): StreamLoader {
- DirectLoadTableDedupStreamLoader (incremental dedupe)
- DirectLoadTableDedupTruncateStreamLoader (full refresh dedupe)
### Step 13.4: Enable Tests
### Advanced Step 4: Enable Tests
**File:** Update `src/test-integration/kotlin/.../component/{DB}TableOperationsTest.kt`
@@ -596,7 +597,7 @@ override fun `upsert tables`() {
}
```
### Step 13.5: Validate
### Advanced Step 5: Validate
**Validate:**
```bash
@@ -611,7 +612,7 @@ $ ./gradlew :destination-{db}:integrationTest # 3 tests should pass
---
## Phase 14: CDC Support (Optional)
## Advanced Phase 3: CDC Support (Optional)
**Goal:** Handle source deletions
@@ -624,7 +625,7 @@ $ ./gradlew :destination-{db}:integrationTest # 3 tests should pass
- **Hard delete**: Remove record from destination
- **Soft delete**: Keep record with deletion timestamp
### Step 14.1: Add CDC Configuration
### Advanced Step 1: Add CDC Configuration
**File:** Update `spec/{DB}Specification.kt`
@@ -659,7 +660,7 @@ override fun makeWithoutExceptionHandling(pojo: {DB}Specification): {DB}Configur
}
```
### Step 14.2: Add CDC Logic to upsertTable()
### Advanced Step 2: Add CDC Logic to upsertTable()
**File:** Update `client/{DB}SqlGenerator.kt`
@@ -726,11 +727,11 @@ private val CDC_DELETED_AT_COLUMN = "_ab_cdc_deleted_at"
- Skip INSERT for deleted records (don't re-insert deleted rows)
- Soft delete: No special clauses (just upsert the deletion record with timestamp)
### Step 14.3: Test CDC
### Advanced Step 3: Test CDC
CDC tests are typically included in integration tests automatically if you have CDC streams configured. No separate test enablement needed - the framework tests CDC if the stream has `_ab_cdc_deleted_at` column.
### Step 14.4: Validate
### Advanced Step 4: Validate
**Validate:**
```bash
@@ -744,7 +745,7 @@ $ ./gradlew :destination-{db}:integrationTest # 3 tests should pass (CDC tested
---
## Phase 15: Optimization & Polish
## Advanced Phase 4: Optimization & Polish
**Goal:** Production-ready performance

View File

@@ -57,7 +57,9 @@ Before starting, you must have:
---
## Step 1: Implement Test Helper Classes
## Testing Phase 1: BasicFunctionalityIntegrationTest
### Testing Step 1: Implement Test Helper Classes
### Step 1.1: Create DestinationDataDumper
@@ -210,7 +212,7 @@ class {DB}Cleaner(
---
## Step 2: Create BasicFunctionalityIntegrationTest Class
### Testing Step 2: Create BasicFunctionalityIntegrationTest Class
### Step 2.1: Understand Required Parameters
@@ -346,7 +348,7 @@ class {DB}BasicFunctionalityTest : BasicFunctionalityIntegrationTest(
---
## Step 3: Configure Test Parameters
### Testing Step 3: Configure Test Parameters
### Quick Reference Table
@@ -531,7 +533,7 @@ AllTypesBehavior.StronglyTyped(
---
## Step 4: Run Tests
### Testing Step 4: Run Tests
### Test Individually
@@ -558,7 +560,7 @@ $ ./gradlew :destination-{db}:integrationTest --tests "*BasicFunctionalityTest"
---
## Step 5: Debug Common Failures
### Testing Step 5: Debug Common Failures
### Test: testAppend fails with "Record mismatch"
@@ -598,7 +600,7 @@ $ ./gradlew :destination-{db}:integrationTest --tests "*BasicFunctionalityTest"
---
## Step 6: Optional Test Customization
### Testing Step 6: Optional Test Customization
### Skip Tests Not Applicable