Files
dinggadingga-backend/src/hiboutik/customer.hiboutik.repository.ts
MastersGame 35a7c27cb7 feat(sync): validate Hiboutik payloads, read French dates and guard deletions
- Hiboutik payloads are zod schemas, validated by HiboutikClient (getOne
  added for single resources: null on 404 or empty answer).
- Hiboutik dates are read as Europe/Paris whatever the server time zone.
- Paginated lists driving deletions are walked twice when they span several
  pages (brands, categories, products); categories are sorted by id.
- A customer missing from the list is deleted only once Hiboutik confirms
  it; completed orders that lost their customer are linked back.
- Each product and its versions are written in one transaction.
- An email removed in Hiboutik is erased in database.
- A 429 is retried with backoff, then stops the synchronization.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 12:20:58 +01:00

54 lines
2.0 KiB
TypeScript

import { Injectable } from '@nestjs/common';
import { HiboutikClient } from './hiboutik.client';
import {
CustomerAddressDetailsHiboutikDto,
customerAddressDetailsHiboutikSchema,
CustomerHiboutikDto,
customerHiboutikSchema,
FullCustomerHiboutikDto,
fullCustomerHiboutikSchema,
} from '../models/hiboutikDTO/customer.hiboutik.dto';
/** Reads customers from the Hiboutik API. */
@Injectable()
export class CustomerHiboutikRepository {
constructor(private readonly hiboutikClient: HiboutikClient) {}
/**
* Customer summaries only: use {@link getCustomerById} for addresses and sales. Sorted by id so that the pages are
* stable, but a customer created or deleted while they are walked can still shift the following ones.
*/
async getCustomers(): Promise<CustomerHiboutikDto[]> {
return this.hiboutikClient.getAllPages('/customers', customerHiboutikSchema, {
order_by: 'customers_id',
sort: 'ASC',
});
}
/**
* Full customer, with its addresses and sales.
* @returns `null` when Hiboutik says the customer does not exist (404 or empty answer).
* @throws on any other failure: an unknown state is never taken for a deleted customer.
*/
async findCustomerById(id: number): Promise<FullCustomerHiboutikDto | null> {
return this.hiboutikClient.getOne(`/customer/${id}`, fullCustomerHiboutikSchema);
}
/** Same as {@link findCustomerById}, but a missing customer throws. */
async getCustomerById(id: number): Promise<FullCustomerHiboutikDto> {
const customer = await this.findCustomerById(id);
if (!customer) throw new Error(`Hiboutik customer ${id} not found`);
return customer;
}
/**
* Address with its company details.
* @throws if the address does not exist.
*/
async getAddressById(id: number): Promise<CustomerAddressDetailsHiboutikDto> {
const address = await this.hiboutikClient.getOne(`/customers_addresses/${id}`, customerAddressDetailsHiboutikSchema);
if (!address) throw new Error(`Hiboutik address ${id} not found`);
return address;
}
}