85 lines
2.7 KiB
TypeScript
85 lines
2.7 KiB
TypeScript
|
|
/**
|
||
|
|
* Japan Post Address Service
|
||
|
|
*
|
||
|
|
* Address lookup service using Japan Post API.
|
||
|
|
* Transforms raw API responses to domain types using domain mappers.
|
||
|
|
*/
|
||
|
|
|
||
|
|
import {
|
||
|
|
Injectable,
|
||
|
|
Inject,
|
||
|
|
BadRequestException,
|
||
|
|
ServiceUnavailableException,
|
||
|
|
} from "@nestjs/common";
|
||
|
|
import { Logger } from "nestjs-pino";
|
||
|
|
import { extractErrorMessage } from "@bff/core/utils/error.util.js";
|
||
|
|
import { JapanPostConnectionService } from "./japanpost-connection.service.js";
|
||
|
|
import { JapanPost } from "@customer-portal/domain/address/providers";
|
||
|
|
import type { AddressLookupResult } from "@customer-portal/domain/address";
|
||
|
|
|
||
|
|
@Injectable()
|
||
|
|
export class JapanPostAddressService {
|
||
|
|
constructor(
|
||
|
|
private readonly connection: JapanPostConnectionService,
|
||
|
|
@Inject(Logger) private readonly logger: Logger
|
||
|
|
) {}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Lookup address by ZIP code
|
||
|
|
*
|
||
|
|
* @param zipCode - ZIP code (with or without hyphen, e.g., "100-0001" or "1000001")
|
||
|
|
* @returns Domain AddressLookupResult with Japanese and romanized address data
|
||
|
|
* @throws BadRequestException if ZIP code format is invalid
|
||
|
|
* @throws ServiceUnavailableException if Japan Post API is unavailable
|
||
|
|
*/
|
||
|
|
async lookupByZipCode(zipCode: string): Promise<AddressLookupResult> {
|
||
|
|
// Normalize ZIP code (remove hyphen)
|
||
|
|
const normalizedZip = zipCode.replace(/-/g, "");
|
||
|
|
|
||
|
|
// Validate format
|
||
|
|
if (!/^\d{7}$/.test(normalizedZip)) {
|
||
|
|
throw new BadRequestException("ZIP code must be 7 digits (e.g., 100-0001)");
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check if service is configured
|
||
|
|
if (!this.connection.isConfigured()) {
|
||
|
|
this.logger.error("Japan Post API not configured");
|
||
|
|
throw new ServiceUnavailableException("Address lookup service is not available");
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
const rawResponse = await this.connection.searchByZipCode(normalizedZip);
|
||
|
|
|
||
|
|
// Use domain mapper for transformation (single transformation point)
|
||
|
|
const result = JapanPost.transformJapanPostSearchResponse(rawResponse);
|
||
|
|
|
||
|
|
this.logger.log("Japan Post address lookup completed", {
|
||
|
|
zipCode: normalizedZip,
|
||
|
|
found: result.count > 0,
|
||
|
|
count: result.count,
|
||
|
|
});
|
||
|
|
|
||
|
|
return result;
|
||
|
|
} catch (error) {
|
||
|
|
// Re-throw known exceptions
|
||
|
|
if (error instanceof BadRequestException || error instanceof ServiceUnavailableException) {
|
||
|
|
throw error;
|
||
|
|
}
|
||
|
|
|
||
|
|
this.logger.error("Japan Post address lookup failed", {
|
||
|
|
zipCode: normalizedZip,
|
||
|
|
error: extractErrorMessage(error),
|
||
|
|
});
|
||
|
|
|
||
|
|
throw new ServiceUnavailableException("Failed to lookup address. Please try again.");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if the Japan Post service is available
|
||
|
|
*/
|
||
|
|
isAvailable(): boolean {
|
||
|
|
return this.connection.isConfigured();
|
||
|
|
}
|
||
|
|
}
|