@bytebrand/export-cars 1.26.40 → 1.26.42
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/dist/configs/autouncle.config.js +22 -0
- package/dist/configs/autouncle.config.js.map +1 -1
- package/dist/exporters/autouncle.exporter.js +10 -0
- package/dist/exporters/autouncle.exporter.js.map +1 -1
- package/dist/services/autouncle-feed-run.d.ts +6 -0
- package/dist/services/autouncle-feed-run.js +11 -1
- package/dist/services/autouncle-feed-run.js.map +1 -1
- package/dist/transformers/autouncle.transformer.d.ts +4 -0
- package/dist/transformers/autouncle.transformer.js +10 -5
- package/dist/transformers/autouncle.transformer.js.map +1 -1
- package/dist/transformers/google-feed-envkv.d.ts +2 -0
- package/dist/transformers/google-feed-envkv.js +9 -1
- package/dist/transformers/google-feed-envkv.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/docs/autouncle/AUDIT.md +103 -97
- package/docs/autouncle/PARTNER-QUESTIONS.md +3 -1
- package/docs/autouncle/RUNBOOK.md +2 -0
- package/docs/criteo/RUNBOOK.md +86 -86
- package/package.json +1 -1
- package/src/configs/autouncle.config.ts +26 -1
- package/src/configs/criteo.config.ts +54 -54
- package/src/exporters/autouncle.exporter.ts +12 -0
- package/src/exporters/criteo.exporter.ts +63 -63
- package/src/services/autouncle-feed-run.ts +19 -2
- package/src/services/criteo-feed-run.ts +253 -253
- package/src/transformers/autouncle.transformer.ts +20 -4
- package/src/transformers/google-feed-envkv.ts +10 -0
- package/test/autouncle-feed-run.test.ts +25 -2
- package/test/autouncle-mapping.test.ts +41 -2
- package/test/criteo-feed-run.test.ts +365 -365
- package/test/criteo-mapping.test.ts +122 -122
- package/test/fixtures/criteo-20-cases.json +581 -581
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
import { Injectable } from '@nestjs/common';
|
|
2
|
-
import { ModuleRef } from '@nestjs/core';
|
|
3
|
-
|
|
4
|
-
import { FsChunkInterface } from '@bytebrand/chunkable';
|
|
5
|
-
import { LoggerService } from '@bytebrand/cronicle-helper';
|
|
6
|
-
|
|
7
|
-
import { CarService, ConfigService, LoaderService, RulesService } from '../services';
|
|
8
|
-
import { FeedGuard } from '../services/feed-guard';
|
|
9
|
-
import { CriteoFeedRun, CriteoSourceCar } from '../services/criteo-feed-run';
|
|
10
|
-
import { Exporter } from '../interfaces/exporter.interface';
|
|
11
|
-
import { CriteoConfig } from '../configs/criteo.config';
|
|
12
|
-
import { CriteoTransformer } from 'src/transformers/criteo.transformer';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* ExportType.CRITEO (ADV-319). Wires the Cronicle/Nest services into CriteoFeedRun, which holds the
|
|
16
|
-
* logic. The output directory is no longer emptied: the new XML replaces the old one only after the
|
|
17
|
-
* fail-closed guard passed.
|
|
18
|
-
*/
|
|
19
|
-
@Injectable()
|
|
20
|
-
export class CriteoExporter implements Exporter {
|
|
21
|
-
constructor(
|
|
22
|
-
private carService: CarService,
|
|
23
|
-
private configService: ConfigService,
|
|
24
|
-
private rulesService: RulesService,
|
|
25
|
-
private loaderService: LoaderService,
|
|
26
|
-
private moduleRef: ModuleRef,
|
|
27
|
-
) { }
|
|
28
|
-
|
|
29
|
-
async export(): Promise<void> {
|
|
30
|
-
const config = this.configService.config as CriteoConfig;
|
|
31
|
-
const [xmlFile] = config.outputFiles;
|
|
32
|
-
const transformer = await this.moduleRef.create(CriteoTransformer);
|
|
33
|
-
|
|
34
|
-
const run = new CriteoFeedRun(
|
|
35
|
-
{
|
|
36
|
-
forEachChunk: fn => this.loaderService.process((chunk: FsChunkInterface) => fn(chunk.data as CriteoSourceCar[])),
|
|
37
|
-
filterCar: source => this.carService.filterCar(source, this.rulesService.rules),
|
|
38
|
-
transform: source => transformer.transform(source),
|
|
39
|
-
guard: new FeedGuard(config.feedGuard),
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
runId: config.runId,
|
|
43
|
-
domain: config.domain,
|
|
44
|
-
sourceDirectory: config.pathesConfig.localDirectory,
|
|
45
|
-
sourceBaseFileName: config.pathesConfig.baseFileName,
|
|
46
|
-
xmlFile,
|
|
47
|
-
stateDir: config.stateDir,
|
|
48
|
-
},
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
try {
|
|
52
|
-
const report = await run.run();
|
|
53
|
-
if (report.stale) {
|
|
54
|
-
console.log(`criteo feed STALE: last good file ${report.lastGoodAgeHours?.toFixed(1)} h old`);
|
|
55
|
-
}
|
|
56
|
-
} catch (error) {
|
|
57
|
-
// main.ts would report a rejected run as {"complete":1,"code":0}; fail the job so the chained
|
|
58
|
-
// upload step does not run (report + lock already handled by the run).
|
|
59
|
-
LoggerService.exitError(`criteo feed not published: ${error?.message ?? error}`);
|
|
60
|
-
process.exit(1);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
1
|
+
import { Injectable } from '@nestjs/common';
|
|
2
|
+
import { ModuleRef } from '@nestjs/core';
|
|
3
|
+
|
|
4
|
+
import { FsChunkInterface } from '@bytebrand/chunkable';
|
|
5
|
+
import { LoggerService } from '@bytebrand/cronicle-helper';
|
|
6
|
+
|
|
7
|
+
import { CarService, ConfigService, LoaderService, RulesService } from '../services';
|
|
8
|
+
import { FeedGuard } from '../services/feed-guard';
|
|
9
|
+
import { CriteoFeedRun, CriteoSourceCar } from '../services/criteo-feed-run';
|
|
10
|
+
import { Exporter } from '../interfaces/exporter.interface';
|
|
11
|
+
import { CriteoConfig } from '../configs/criteo.config';
|
|
12
|
+
import { CriteoTransformer } from 'src/transformers/criteo.transformer';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* ExportType.CRITEO (ADV-319). Wires the Cronicle/Nest services into CriteoFeedRun, which holds the
|
|
16
|
+
* logic. The output directory is no longer emptied: the new XML replaces the old one only after the
|
|
17
|
+
* fail-closed guard passed.
|
|
18
|
+
*/
|
|
19
|
+
@Injectable()
|
|
20
|
+
export class CriteoExporter implements Exporter {
|
|
21
|
+
constructor(
|
|
22
|
+
private carService: CarService,
|
|
23
|
+
private configService: ConfigService,
|
|
24
|
+
private rulesService: RulesService,
|
|
25
|
+
private loaderService: LoaderService,
|
|
26
|
+
private moduleRef: ModuleRef,
|
|
27
|
+
) { }
|
|
28
|
+
|
|
29
|
+
async export(): Promise<void> {
|
|
30
|
+
const config = this.configService.config as CriteoConfig;
|
|
31
|
+
const [xmlFile] = config.outputFiles;
|
|
32
|
+
const transformer = await this.moduleRef.create(CriteoTransformer);
|
|
33
|
+
|
|
34
|
+
const run = new CriteoFeedRun(
|
|
35
|
+
{
|
|
36
|
+
forEachChunk: fn => this.loaderService.process((chunk: FsChunkInterface) => fn(chunk.data as CriteoSourceCar[])),
|
|
37
|
+
filterCar: source => this.carService.filterCar(source, this.rulesService.rules),
|
|
38
|
+
transform: source => transformer.transform(source),
|
|
39
|
+
guard: new FeedGuard(config.feedGuard),
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
runId: config.runId,
|
|
43
|
+
domain: config.domain,
|
|
44
|
+
sourceDirectory: config.pathesConfig.localDirectory,
|
|
45
|
+
sourceBaseFileName: config.pathesConfig.baseFileName,
|
|
46
|
+
xmlFile,
|
|
47
|
+
stateDir: config.stateDir,
|
|
48
|
+
},
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
const report = await run.run();
|
|
53
|
+
if (report.stale) {
|
|
54
|
+
console.log(`criteo feed STALE: last good file ${report.lastGoodAgeHours?.toFixed(1)} h old`);
|
|
55
|
+
}
|
|
56
|
+
} catch (error) {
|
|
57
|
+
// main.ts would report a rejected run as {"complete":1,"code":0}; fail the job so the chained
|
|
58
|
+
// upload step does not run (report + lock already handled by the run).
|
|
59
|
+
LoggerService.exitError(`criteo feed not published: ${error?.message ?? error}`);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -4,8 +4,11 @@ import { ImboImageResponseInterface } from '@bytebrand/chunkable';
|
|
|
4
4
|
import { DataSchemes } from '@bytebrand/car-schema';
|
|
5
5
|
|
|
6
6
|
import { AutouncleJsonBuilder } from '../formats/autouncle-json.builder';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
AUTOUNCLE_ENVKV_ATTRIBUTES, AutouncleAddress, AutouncleExclusion, getAutouncleExclusion, mapAutouncleItem,
|
|
9
|
+
} from '../transformers/autouncle.transformer';
|
|
8
10
|
import { FeedGuard, FeedGuardError, LastGood, inspectSourceSnapshot } from './feed-guard';
|
|
11
|
+
import { PublicationGate } from './publication-gate';
|
|
9
12
|
|
|
10
13
|
export interface AutouncleSourceCar {
|
|
11
14
|
car: DataSchemes.Car;
|
|
@@ -26,6 +29,8 @@ export interface AutouncleRunDeps {
|
|
|
26
29
|
imageStats: () => Record<string, number>;
|
|
27
30
|
translate: (key: string) => string;
|
|
28
31
|
guard: FeedGuard;
|
|
32
|
+
// EnVKV C8 publication gate (CBD enrichment log); absent = off
|
|
33
|
+
gate?: Pick<PublicationGate, 'mode' | 'estimatedAttributes'>;
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
export interface AutouncleRunOptions {
|
|
@@ -54,6 +59,8 @@ export interface AutouncleRunReport {
|
|
|
54
59
|
excluded: Record<string, number>;
|
|
55
60
|
imagesPerItem: { min: number | null; max: number | null; total: number };
|
|
56
61
|
images: Record<string, number>;
|
|
62
|
+
// published items with an estimated EnVKV value per attribute; with gate enforce those were sent empty
|
|
63
|
+
envkv: { gate: 'off' | 'audit' | 'enforce'; estimated: Record<string, number> };
|
|
57
64
|
lastGood: LastGood | null;
|
|
58
65
|
lastGoodAgeHours: number | null;
|
|
59
66
|
stale: boolean;
|
|
@@ -93,6 +100,7 @@ export class AutouncleFeedRun {
|
|
|
93
100
|
excluded: {},
|
|
94
101
|
imagesPerItem: { min: null, max: null, total: 0 },
|
|
95
102
|
images: {},
|
|
103
|
+
envkv: { gate: this.deps.gate?.mode ?? 'off', estimated: {} },
|
|
96
104
|
lastGood,
|
|
97
105
|
lastGoodAgeHours: guard.staleHours(lastGood, startedAt),
|
|
98
106
|
stale: guard.isStale(lastGood, startedAt),
|
|
@@ -141,11 +149,20 @@ export class AutouncleFeedRun {
|
|
|
141
149
|
continue;
|
|
142
150
|
}
|
|
143
151
|
const images = await this.deps.resolveImages(source);
|
|
144
|
-
const
|
|
152
|
+
const estimated = (this.deps.gate ? this.deps.gate.estimatedAttributes(source.car) : [])
|
|
153
|
+
.filter(attribute => AUTOUNCLE_ENVKV_ATTRIBUTES.includes(attribute));
|
|
154
|
+
const mapped = mapAutouncleItem(source.car, {
|
|
155
|
+
domain: this.options.domain,
|
|
156
|
+
address,
|
|
157
|
+
images,
|
|
158
|
+
translate: this.deps.translate,
|
|
159
|
+
withheldEnvkv: this.deps.gate?.mode === 'enforce' ? estimated : [],
|
|
160
|
+
});
|
|
145
161
|
if (mapped.ok === false) {
|
|
146
162
|
increment(report.excluded, mapped.reason);
|
|
147
163
|
continue;
|
|
148
164
|
}
|
|
165
|
+
estimated.forEach(attribute => increment(report.envkv.estimated, attribute));
|
|
149
166
|
await builder.addItem(mapped.item, first);
|
|
150
167
|
first = false;
|
|
151
168
|
dealers.add(ownerId);
|