新增 business-mirror:follow-upstream-columns:先把数据字典刷新到最新,再把上游 视图里新增、当前数据集还没同步的字段追加进镜像定义并立刻建列,下一次常规同步 就会带回数据。只做新增,不删除/不改写已有字段;字段名带 password/secret/token/ cipher/credential 等敏感关键字的一律跳过。已登记到发布 cron(每天 06:40)。 页面侧:config/upstream_mirrors.php 的 auto_follow_columns 用「页面 => 数据集」 登记,FollowedUpstreamColumns 服务把 followed_columns 变成表格列;这类列只展示、 不生成查询条件(后端还没有对应筛选口径)。 数据集自带的数据变动仍然由每分钟的 business-mirror:sync-tables 增量同步。
99 lines
3.6 KiB
PHP
99 lines
3.6 KiB
PHP
<?php
|
|
|
|
namespace Tests\Unit\EcommerceFinance;
|
|
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Schema;
|
|
use Tests\TestCase;
|
|
|
|
class FollowUpstreamColumnsTest extends TestCase
|
|
{
|
|
private const DATASET = 'demo.accounts';
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
config([
|
|
'database.connections.business_mirror' => [
|
|
'driver' => 'sqlite', 'database' => ':memory:', 'prefix' => '',
|
|
],
|
|
'upstream_mirrors.connection' => 'business_mirror',
|
|
'upstream_mirrors.datasets' => [],
|
|
'upstream_mirrors.auto_follow_columns' => ['demo/page' => self::DATASET],
|
|
]);
|
|
DB::purge('business_mirror');
|
|
|
|
DB::table('business_mirror_datasets')->insert([
|
|
'dataset_key' => self::DATASET,
|
|
'source_key' => 'demo',
|
|
'definition' => json_encode([
|
|
'source_key' => 'demo', 'schema' => 'external', 'object' => 'accounts_v1',
|
|
'target' => 'demo_accounts', 'keys' => ['id'], 'columns' => ['id', 'name'],
|
|
], JSON_THROW_ON_ERROR),
|
|
'sync_enabled' => true,
|
|
'initial_sync_approved' => true,
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
]);
|
|
|
|
$this->insertCatalogColumn('id', 1);
|
|
$this->insertCatalogColumn('name', 2);
|
|
}
|
|
|
|
private function insertCatalogColumn(string $column, int $position): void
|
|
{
|
|
DB::table('upstream_schema_columns')->insert([
|
|
'source_key' => 'demo',
|
|
'schema_name' => 'external',
|
|
'object_name' => 'accounts_v1',
|
|
'object_type' => 'view',
|
|
'column_name' => $column,
|
|
'ordinal_position' => $position,
|
|
'data_type' => 'varchar',
|
|
'max_length' => 191,
|
|
'synchronized_at' => now(),
|
|
]);
|
|
}
|
|
|
|
public function test_it_adds_new_upstream_columns_and_skips_sensitive_ones(): void
|
|
{
|
|
$this->insertCatalogColumn('platform', 3);
|
|
$this->insertCatalogColumn('passwordCiphertext', 4);
|
|
|
|
$this->artisan('business-mirror:follow-upstream-columns')->assertSuccessful();
|
|
|
|
$definition = json_decode(
|
|
(string) DB::table('business_mirror_datasets')->where('dataset_key', self::DATASET)->value('definition'),
|
|
true,
|
|
);
|
|
|
|
$this->assertSame(['id', 'name', 'platform'], $definition['columns']);
|
|
$this->assertSame(['platform'], $definition['followed_columns']);
|
|
$this->assertTrue(Schema::connection('business_mirror')->hasColumn('demo_accounts', 'platform'));
|
|
$this->assertFalse(Schema::connection('business_mirror')->hasColumn('demo_accounts', 'passwordCiphertext'));
|
|
}
|
|
|
|
public function test_it_is_idempotent_when_there_is_nothing_new(): void
|
|
{
|
|
$this->insertCatalogColumn('platform', 3);
|
|
$this->artisan('business-mirror:follow-upstream-columns')->assertSuccessful();
|
|
|
|
$definition = json_decode(
|
|
(string) DB::table('business_mirror_datasets')->where('dataset_key', self::DATASET)->value('definition'),
|
|
true,
|
|
);
|
|
$this->assertSame(['id', 'name', 'platform'], $definition['columns']);
|
|
|
|
// 再跑一次不会重复追加,也不会重复记录。
|
|
$this->artisan('business-mirror:follow-upstream-columns')->assertSuccessful();
|
|
|
|
$again = json_decode(
|
|
(string) DB::table('business_mirror_datasets')->where('dataset_key', self::DATASET)->value('definition'),
|
|
true,
|
|
);
|
|
$this->assertSame(['id', 'name', 'platform'], $again['columns']);
|
|
$this->assertSame(['platform'], $again['followed_columns']);
|
|
}
|
|
}
|