@bytebrand/export-cars 1.26.4 → 1.26.6
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/exporters/fb.exporter.js +9 -2
- package/dist/exporters/fb.exporter.js.map +1 -1
- package/dist/services/images.service.d.ts +0 -3
- package/dist/services/images.service.js +4 -21
- package/dist/services/images.service.js.map +1 -1
- package/dist/transformers/fb.transformer.js +0 -6
- package/dist/transformers/fb.transformer.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/exporters/fb.exporter.ts +22 -2
- package/src/services/images.service.ts +9 -38
- package/src/transformers/fb.transformer.ts +1 -12
package/package.json
CHANGED
|
@@ -8,6 +8,11 @@ import { FbXmlBuilder } from '../formats/fb-xml.builder';
|
|
|
8
8
|
import { ModuleRef } from '@nestjs/core';
|
|
9
9
|
import { FbTransformer } from '../transformers/fb.transformer';
|
|
10
10
|
import { FbXmlItem } from '../interfaces';
|
|
11
|
+
import { META_PUSH_PRIORITY_ALLOWED_VALUES, META_PUSH_PRIORITY_STANDARD } from '../constants/fb.constants';
|
|
12
|
+
|
|
13
|
+
// dealer_priority_1 first, then dealer_priority_2, then everyone else (standard/unlisted) —
|
|
14
|
+
// mirrors the CSV's own priority tiers (see MappingService.getMetaPushPriorityMapping).
|
|
15
|
+
const PRIORITY_ORDER = [...META_PUSH_PRIORITY_ALLOWED_VALUES, META_PUSH_PRIORITY_STANDARD];
|
|
11
16
|
|
|
12
17
|
@Injectable()
|
|
13
18
|
export class FbExporter implements Exporter {
|
|
@@ -29,6 +34,12 @@ export class FbExporter implements Exporter {
|
|
|
29
34
|
|
|
30
35
|
const fbTransformer = await this.moduleRef.create(FbTransformer);
|
|
31
36
|
|
|
37
|
+
// Buffered (not streamed straight to XML) so the whole feed can be sorted by dealer
|
|
38
|
+
// priority before writing — see the sort below. FbXmlItem doesn't declare custom_label_4
|
|
39
|
+
// (that interface predates ADV-159/161); the transformer always sets it, so extend locally
|
|
40
|
+
// rather than touching the shared interface other export types also cast through.
|
|
41
|
+
const cars: (FbXmlItem & { custom_label_4: string })[] = [];
|
|
42
|
+
|
|
32
43
|
await this.loaderService.process(async (chunk: FsChunkInterface) => {
|
|
33
44
|
const chunkData = chunk.data.filter(car => this.carService.filterCar(car, this.rulesService.rules));
|
|
34
45
|
chunk.data = null;
|
|
@@ -42,15 +53,24 @@ export class FbExporter implements Exporter {
|
|
|
42
53
|
console.timeEnd(`Transform ${data.car._id}`);
|
|
43
54
|
|
|
44
55
|
if (!car) {
|
|
45
|
-
console.log(`Skip car ${data.car._id} — no real images
|
|
56
|
+
console.log(`Skip car ${data.car._id} — no real images`);
|
|
46
57
|
continue;
|
|
47
58
|
}
|
|
48
59
|
|
|
49
|
-
|
|
60
|
+
cars.push(car as FbXmlItem & { custom_label_4: string });
|
|
50
61
|
this.carService.counter.countCar(data.images);
|
|
51
62
|
}
|
|
52
63
|
});
|
|
53
64
|
|
|
65
|
+
// ADV-161-follow-up: order by the meta push priority sheet (metaPushPriorityCsvUrl)
|
|
66
|
+
// instead of filtering non-listed dealers out — dealer_priority_1 first, then
|
|
67
|
+
// dealer_priority_2, then everyone else, in whatever order they were transformed.
|
|
68
|
+
cars.sort((a, b) => PRIORITY_ORDER.indexOf(a.custom_label_4) - PRIORITY_ORDER.indexOf(b.custom_label_4));
|
|
69
|
+
|
|
70
|
+
for (const car of cars) {
|
|
71
|
+
await fbXmlBuilder.addItem(car);
|
|
72
|
+
}
|
|
73
|
+
|
|
54
74
|
await fbXmlBuilder.buildEnd();
|
|
55
75
|
console.log(this.carService.counter.getCountedCars());
|
|
56
76
|
}
|
|
@@ -85,8 +85,6 @@ export class ImagesService {
|
|
|
85
85
|
'https://images.auto.de/carimage/noimage/pFyfBsqBHEso/large-cached.jpeg',
|
|
86
86
|
];
|
|
87
87
|
|
|
88
|
-
private hasLoggedMarketingImageDebug = false;
|
|
89
|
-
|
|
90
88
|
constructor(private readonly configService: ConfigService, private readonly userService: UserService) { }
|
|
91
89
|
|
|
92
90
|
async getImages(car: DataSchemes.Car, images: ImboImageResponseInterface[], imageType: ImageType, ignoreDefImages: boolean): Promise<Array<string>> {
|
|
@@ -178,8 +176,6 @@ export class ImagesService {
|
|
|
178
176
|
}
|
|
179
177
|
|
|
180
178
|
async getMarketingImages(carId: string, images: ImboImageResponseInterface[], ignoreDefImages: boolean): Promise<string[]> {
|
|
181
|
-
const imageUrl = this.configService.config.marketingImageUrl;
|
|
182
|
-
|
|
183
179
|
const transformedImages = this.transformImagesArrayIntoObject(images);
|
|
184
180
|
// fbMarketingImageId is a hardcoded per-environment id in config.ts and has drifted from
|
|
185
181
|
// the real meta_aia config at least once (dev's id pointed at a stale, never-populated "fb"
|
|
@@ -193,18 +189,15 @@ export class ImagesService {
|
|
|
193
189
|
return ignoreDefImages ? [] : [...this.DEFAULT_IMAGES];
|
|
194
190
|
}
|
|
195
191
|
|
|
196
|
-
|
|
192
|
+
// marketingImageUrl (config.ts environmentConfig[env].imageUrl) points at /cache_test on
|
|
193
|
+
// dev — a leftover from the old "fb" channel, not where image-generator publishes meta_aia
|
|
194
|
+
// composites (always under /generated_images on both dev and prod). Build the meta_aia base
|
|
195
|
+
// from the plain image host instead of trusting that per-env path.
|
|
196
|
+
const imageUrl = config.channelCode === 'meta_aia'
|
|
197
|
+
? `${this.configService.config.imageUrl}/generated_images`
|
|
198
|
+
: this.configService.config.marketingImageUrl;
|
|
197
199
|
|
|
198
|
-
|
|
199
|
-
this.hasLoggedMarketingImageDebug = true;
|
|
200
|
-
console.log(
|
|
201
|
-
'getMarketingImages debug —',
|
|
202
|
-
'config.id:', config.id,
|
|
203
|
-
'config.channelCode:', config.channelCode,
|
|
204
|
-
'requested imageCodes per slot:', JSON.stringify(marketingImageConfigs.map(c => c.marketingImageCodes)),
|
|
205
|
-
'photo codes available on this car:', JSON.stringify(Object.keys(transformedImages)),
|
|
206
|
-
);
|
|
207
|
-
}
|
|
200
|
+
const marketingImageConfigs = config.marketingImages.map(i => ({ marketingImageCodes: i.imageCodes }));
|
|
208
201
|
|
|
209
202
|
const imageWithUrls: Partial<ImboImageResponseInterface & { url: string }>[] = [];
|
|
210
203
|
for (let i = 0; i < marketingImageConfigs.length; i++) {
|
|
@@ -213,15 +206,9 @@ export class ImagesService {
|
|
|
213
206
|
const imageCode = marketingImage.marketingImageCodes[i];
|
|
214
207
|
const imageId = transformedImages[imageCode];
|
|
215
208
|
if (imageId) {
|
|
216
|
-
const url = this.getImagePathWithExtension(imageUrl, carId, imageId, config);
|
|
217
|
-
// meta_aia cards are composited async and may not exist yet for a given car —
|
|
218
|
-
// verify the file is real before publishing the link, not just the placeholder.
|
|
219
|
-
if (config.channelCode === 'meta_aia' && !(await this.imageExists(url))) {
|
|
220
|
-
break;
|
|
221
|
-
}
|
|
222
209
|
imageWithUrls.push({
|
|
223
210
|
imageCodePredicted: imageCode,
|
|
224
|
-
url,
|
|
211
|
+
url: this.getImagePathWithExtension(imageUrl, carId, imageId, config),
|
|
225
212
|
});
|
|
226
213
|
break;
|
|
227
214
|
}
|
|
@@ -238,22 +225,6 @@ export class ImagesService {
|
|
|
238
225
|
return imageWithUrls.map(i => i.url);
|
|
239
226
|
}
|
|
240
227
|
|
|
241
|
-
// Imbo serves a 400x300 JPEG placeholder (measured 7996 bytes on both auto.de and
|
|
242
|
-
// autode-dev.de) with a 404 status when a transformation isn't ready yet. nginx in front of
|
|
243
|
-
// Imbo has been seen caching that placeholder body regardless of status code (see
|
|
244
|
-
// nginx-image-cache-negative-caching), so check Content-Length rather than trusting the
|
|
245
|
-
// HTTP status alone.
|
|
246
|
-
private readonly PLACEHOLDER_MAX_BYTES = 8000;
|
|
247
|
-
|
|
248
|
-
private async imageExists(url: string): Promise<boolean> {
|
|
249
|
-
try {
|
|
250
|
-
const { status, headers } = await axios.head(url, { timeout: 5000 });
|
|
251
|
-
return status === 200 && Number(headers['content-length'] ?? 0) > this.PLACEHOLDER_MAX_BYTES;
|
|
252
|
-
} catch {
|
|
253
|
-
return false;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
228
|
private transformImagesArrayIntoObject(images: ImboImageResponseInterface[]): { [imageCode: string]: string } {
|
|
258
229
|
return images.reduce((result, image) => {
|
|
259
230
|
const imageCode = image?.imageCodePredicted?.split('-')[0];
|
|
@@ -32,7 +32,7 @@ import { ConfigService, ImagesService, MappingService } from '../services';
|
|
|
32
32
|
import { FbEquipment } from './fb-equipment.transformer';
|
|
33
33
|
import { isValidVin } from '../utils';
|
|
34
34
|
import { UserService } from 'src/services';
|
|
35
|
-
import {
|
|
35
|
+
import { ImageType } from '../enums';
|
|
36
36
|
import {
|
|
37
37
|
DEFAULT_ADDR1,
|
|
38
38
|
DEFAULT_CITY,
|
|
@@ -84,17 +84,6 @@ export class FbTransformer {
|
|
|
84
84
|
const autodeId = toString(get(car, '_id', ''));
|
|
85
85
|
const year = this.getYear(car);
|
|
86
86
|
const ownerId = get(car, 'metaData.ownerId', null);
|
|
87
|
-
|
|
88
|
-
// ADV-161-follow-up: on prod, only publish dealers listed in the meta push priority sheet
|
|
89
|
-
// (metaPushPriorityCsvUrl). Dev dealer ids don't match that prod sheet, so this stays a
|
|
90
|
-
// no-op there — every car passes through, same as before this filter existed.
|
|
91
|
-
if (this.configService.config.environment === Environment.PRODUCTION) {
|
|
92
|
-
const priorities = await this.mappingService.getMetaPushPriorityMapping();
|
|
93
|
-
if (!priorities.some(priority => priority.dealer_id === ownerId)) {
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
87
|
const dealerInfo = this.getDealerItems(car);
|
|
99
88
|
// console.time(`Address ${car._id}`);
|
|
100
89
|
const address = await this.getAddress(car, owner);
|