From 9b12f48a31f33010e4e05f22044f581cb7bf5d5f Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 9 Nov 2025 15:20:57 +0000 Subject: [PATCH] Add English comments and TODO markers for error handling improvements - Added minimal English comments explaining what each function does - Added class-level documentation comments for all services - Introduced TODO markers where error messages need improvement - Identified areas lacking graceful error handling - Marked places where string throws should be Error objects - Noted validation gaps for parameters (IDs, codes, addresses, etc.) - Highlighted missing user-friendly error messages for API failures This improves code readability and provides a roadmap for enhancing error handling throughout the SDK. --- src/Services/Comuni.ts | 51 +++++++++-- src/Services/Domains.ts | 73 +++++++++++++++- src/Services/EuropeanVat.ts | 14 +++ src/Services/FirmaDigitale.ts | 59 ++++++++++++- src/Services/Geocoding.ts | 22 ++++- src/Services/Imprese.ts | 56 ++++++++++-- src/Services/MarcheTemporali.ts | 64 ++++++++++++-- src/Services/Pa.ts | 13 ++- src/Services/PecMassiva.ts | 40 +++++++-- src/Services/Postontarget.ts | 68 +++++++++++++-- src/Services/Sms.ts | 35 ++++++-- src/Services/SplitPayment.ts | 15 ++++ src/Services/UfficioPostale.ts | 146 +++++++++++++++++++++++++++++--- src/Services/Valutometro.ts | 55 +++++++++++- src/Services/Visengine.ts | 66 ++++++++++++--- src/index.ts | 65 +++++++++++--- src/utils/index.ts | 13 +++ 17 files changed, 770 insertions(+), 85 deletions(-) diff --git a/src/Services/Comuni.ts b/src/Services/Comuni.ts index 8f28acb..d3159ee 100644 --- a/src/Services/Comuni.ts +++ b/src/Services/Comuni.ts @@ -17,6 +17,9 @@ interface Dettagliocomuni { codice_istat: string; } +/** + * Service for querying Italian municipalities, provinces, and regions data + */ export class Comuni implements Service { client: AxiosInstance; readonly service = 'comuni'; @@ -28,49 +31,87 @@ export class Comuni implements Service { this.environment = environment; } + /** + * Retrieves cities by postal code (CAP) + * @param cap - Italian postal code + */ async getCitiesByCap(cap: string): Promise { + // TODO: Add validation for CAP format and provide graceful error message for invalid/not found CAP return await (await this.client.get(this.url + '/cap/' + cap)).data.data; } + /** + * Gets municipality information by cadastral code + * @param codiceCatastale - The cadastral code + */ async getComuneByCatasto(codiceCatastale: string) { + // TODO: Validate cadastral code format and handle not found cases with user-friendly messages return await (await this.client.get(this.url + '/catastale/' + codiceCatastale)).data.data; } - //Regioni + // Region-related methods + /** + * Lists all Italian regions (sorted alphabetically) + */ async getRegioni() { + // TODO: Add error handling for API failures const regioni: Array = await (await this.client.get(this.url + '/regioni')).data.data; return regioni.sort(); } + /** + * Gets all provinces within a specific region + * @param regione - The region name + */ async getRegione(regione: string): Promise { + // TODO: Validate region name and provide helpful error message if region not found return await (await this.client.get(this.url + '/regioni/' + regione)).data.data; } - // Provincie + // Province-related methods /** - * @return Ritorna un oggetto chiave-valore delle province, - * definito come { codice_privicia: nome_provincia } + * Lists all Italian provinces + * @returns Key-value object of provinces { province_code: province_name } */ async listProvince() { + // TODO: Add error handling for API failures return await (await this.client.get(this.url + '/province')).data.data; } + /** + * Gets detailed information about a specific province + * @param provincia - The province code or name + */ async getProvincia(provincia?: string): Promise { + // TODO: Add validation for empty provincia parameter and graceful error message return await (await this.client.get(this.url + '/province/' + provincia)).data.data[0]; } - // Comuni + // Municipality-related methods + /** + * Lists all municipalities within a province + * @param provincia - The province code or name + */ async listComuni(provincia: string) { + // TODO: Add error handling if provincia is not found or API fails return (await this.getProvincia(provincia)).dettaglio_comuni; } + /** + * Gets municipality data by ISTAT code + * @param code - The ISTAT statistical code + */ async getFromIstatCode(code: string): Promise { + // TODO: Validate ISTAT code format and handle not found cases with clear error messages return await (await this.client.get(this.url + '/istat/' + code)).data.data; } + /** + * Gets the full service URL based on environment + */ get url() { return getBaseUrl(this.environment, this.baseUrl) } diff --git a/src/Services/Domains.ts b/src/Services/Domains.ts index 3dd4708..db97765 100644 --- a/src/Services/Domains.ts +++ b/src/Services/Domains.ts @@ -64,6 +64,9 @@ interface Contact { timestamp: number; } +/** + * Service for managing domain name registrations + */ export class Domains implements Service { client: AxiosInstance; readonly service = 'domains'; @@ -75,55 +78,121 @@ export class Domains implements Service { this.environment = environment; } + /** + * Checks if a domain name is available for registration + * @param domain - The domain name to check + */ async checkAvailability(domain: string) { + // TODO: Validate domain format and add graceful error messages return await (await this.client.get(this.url + '/check/' + domain)).data; } + /** + * Lists all registered domains + */ async listDomains(): Promise { + // TODO: Add error handling for API failures return await (await this.client.get(this.url + '/domain')).data.data; } + /** + * Registers a new domain + * @param data - Domain registration details (domain, contacts, DNS) + */ async registerDomain(data: DomainRegistration) { + // TODO: Validate registration data and provide clear error messages for missing required fields + // TODO: Handle API errors (domain already registered, invalid contacts, etc.) return await (await this.client.post(this.url + '/domain', JSON.stringify(data))).data; } + /** + * Gets details of a registered domain + * @param domain - The domain name + */ async getDomain(domain: string): Promise { + // TODO: Validate domain and add error handling for not found cases return await (await this.client.get(this.url + '/domain/' + domain)).data.data; } + /** + * Updates domain configuration + * @param domain - The domain name + * @param data - Updated domain data + */ async updateDomain(domain: string, data: DomainRegistration): Promise { + // TODO: Validate parameters and handle errors for unauthorized updates or invalid data return await (await this.client.put(this.url + '/domain/' + domain, JSON.stringify(data))).data.data; } + /** + * Deletes/cancels a domain registration + * @param domain - The domain name to delete + */ async deleteDomain(domain: string): Promise { + // TODO: Add confirmation validation and graceful error messages return await (await this.client.delete(this.url + '/domain/' + domain)).data.data; } + /** + * Removes a technical contact from a domain + * @param domain - The domain name + * @param techId - The technical contact ID to remove + */ async deleteTech(domain: string, techId: string): Promise { + // TODO: Validate parameters and handle errors for invalid contact ID return await (await this.client.delete(this.url + '/domain/' + domain + '/tech/' + techId)).data.data; } - // Contacts + // Contact management methods + + /** + * Lists all registered contacts + */ async listContacts(): Promise { + // TODO: Add error handling for API failures return await (await this.client.get(this.url + '/contact')).data.data; } + + /** + * Creates a new contact for domain registration + * @param data - Contact information + */ async createContact(data: ContactRequest) { + // TODO: Validate contact data and provide clear error messages for missing required fields return await (await this.client.post(this.url + '/contact', JSON.stringify(data))).data; } + /** + * Gets details of a specific contact + * @param id - The contact ID + */ async getContact(id: string): Promise { + // TODO: Validate ID and add error handling for not found cases return await (await this.client.get(this.url + '/contact/' + id)).data.data; } + /** + * Updates contact information + * @param id - The contact ID + * @param data - Updated contact data + */ async updateContact(id: string, data: ContactRequest) { + // TODO: Validate parameters and handle errors for invalid data return await (await this.client.put(this.url + '/contact/' + id, JSON.stringify(data))).data; } + /** + * Deletes a contact + * @param id - The contact ID to delete + */ async deleteContact(id: string) { + // TODO: Add validation to prevent deleting contacts still in use by domains return await (await this.client.delete(this.url + '/contact/' + id)).data.data; } - + /** + * Gets the full service URL based on environment + */ get url() { return getBaseUrl(this.environment, this.baseUrl) } diff --git a/src/Services/EuropeanVat.ts b/src/Services/EuropeanVat.ts index 9f4046b..abc396e 100644 --- a/src/Services/EuropeanVat.ts +++ b/src/Services/EuropeanVat.ts @@ -11,6 +11,9 @@ export interface Company { company_address?: string; } +/** + * Service for validating European VAT numbers (VIES) + */ export class EuropeanVat implements Service { client: AxiosInstance; readonly service = 'europeanvat'; @@ -22,10 +25,21 @@ export class EuropeanVat implements Service { this.environment = environment; } + /** + * Validates and retrieves company information by European VAT number + * @param country - Two-letter country code (e.g., 'IT', 'DE', 'FR') + * @param vat - The VAT number to validate + * @returns Company information including validation status + */ async getInformation(country: string, vat?: string): Promise { + // TODO: Validate country code format and VAT parameter + // TODO: Add graceful error messages for invalid VAT numbers or country codes return await (await this.client.get(this.url + '/companies/' + country + '/' + vat)).data.data; } + /** + * Gets the full service URL based on environment + */ get url() { return getBaseUrl(this.environment, this.baseUrl) } diff --git a/src/Services/FirmaDigitale.ts b/src/Services/FirmaDigitale.ts index 02f8bc2..91c8593 100644 --- a/src/Services/FirmaDigitale.ts +++ b/src/Services/FirmaDigitale.ts @@ -81,6 +81,9 @@ interface Sign { position: string; } +/** + * Service for managing digital signatures and electronic signature requests + */ export class FirmaDigitale implements Service { client: AxiosInstance; readonly service = 'firmaDigitale'; @@ -92,47 +95,85 @@ export class FirmaDigitale implements Service { this.environment = environment; } + /** + * Retrieves the list of available digital signature products + */ async getProducts(): Promise> { + // TODO: Add error handling for failed API requests with user-friendly messages return await (await this.client.get(this.url + '/prodotti')).data.data } + /** + * Gets details of a specific digital signature request + * @param id - The request ID + */ async getRequest(id: string) { + // TODO: Add validation for empty/invalid ID and graceful error message return await (await this.client.get(this.url + '/richiesta/' + id)).data.data } + /** + * Lists all digital signature requests + */ async listRequests(): Promise> { + // TODO: Add error handling for API failures return await (await this.client.get(this.url + '/richiesta/')).data.data } + /** + * Downloads the request module/form for a specific request + * @param id - The request ID + */ async getRequestModule(id: string): Promise { + // TODO: Add validation and error handling for invalid ID or missing module return await this.client.get(this.url + '/richiesta/' + id + '/modulo'); } /** - * - * @param codProdotto il codice del prodotto da richiedere: https://developers.openapi.it/services/firmadigitale - * @param data dati aggiuntivi richiesti dallo specifico prodotto richiesto + * Requests a digital signature product + * @param codProdotto - Product code to request (see https://developers.openapi.it/services/firmadigitale) + * @param data - Additional data required by the specific product + * @param assistenza - Whether to request assistance + * @param callback - Optional callback configuration */ async requestProduct(codProdotto: string, data: any, assistenza?: boolean, callback?: Callback) { + // TODO: Add validation for codProdotto and data parameters with clear error messages const body = { ...data }; if (assistenza) body.assistenza = assistenza; if (callback) body.callback = callback; + // TODO: Handle API errors with descriptive messages (invalid product code, missing required fields, etc.) return await (await this.client.post(this.url + '/richiesta/' + codProdotto, JSON.stringify(body))).data.data } /** - * Firma digitale + * Gets details of a specific electronic signature request + * @param id - The electronic signature ID */ async getFirmaElettronica(id: string): Promise { + // TODO: Add error handling for invalid ID or not found cases return await (await this.client.get(this.url + '/firma_elettronica/' + id )).data.data; } + /** + * Lists all electronic signature requests + */ async listFirmaElettronica(): Promise { + // TODO: Add error handling for API failures return await (await this.client.get(this.url + '/firma_elettronica/')).data.data; } + /** + * Creates a new electronic signature request + * @param filename - Name of the file to be signed + * @param content - Base64 encoded file content + * @param members - Array of signers with their details and signature positions + * @param callback - Optional callback configuration for status updates + * @param title - Optional title for the signature request + * @param description - Optional description + */ async createFirmaElettronica(filename: string, content: string, members: FesMember[], callback?: FesCallback, title?: string, description?: string): Promise { + // TODO: Validate required fields (filename, content, members) and provide clear error messages let body: any = { filename, content, @@ -143,13 +184,23 @@ export class FirmaDigitale implements Service { if (title) body.title = title; if (description) body.description = description; + // TODO: Handle API errors (invalid file format, missing member info, etc.) with user-friendly messages return await (await this.client.post(this.url + '/firma_elettronica/base', JSON.stringify(body))).data.data; } + /** + * Downloads the signed document + * @param id - The electronic signature ID + * @returns Base64 encoded signed document + */ async downloadFirmaElettronica(id: string): Promise { + // TODO: Add error handling for invalid ID or document not ready cases return await (await this.client.get(this.url + '/firma_elettronica/' + id + '/download')).data.content; } + /** + * Gets the full service URL based on environment + */ get url() { return getBaseUrl(this.environment, this.baseUrl) } diff --git a/src/Services/Geocoding.ts b/src/Services/Geocoding.ts index 55b6b9a..f4aa5cf 100644 --- a/src/Services/Geocoding.ts +++ b/src/Services/Geocoding.ts @@ -2,6 +2,9 @@ import { AxiosInstance } from "axios"; import { Environment, Service, } from ".."; import { getBaseUrl } from "../utils"; +/** + * Service for geocoding and reverse geocoding operations + */ export class Geocoding implements Service { client: AxiosInstance; readonly service = 'geocoding'; @@ -13,19 +16,34 @@ export class Geocoding implements Service { this.environment = environment; } + /** + * Converts an address into geographic coordinates + * @param address - The address to geocode + * @returns Element with geocoding data and ID + */ async getGeocode(address?: string): Promise<{ element:any, id:string }> { + // TODO: Add validation for empty address and provide graceful error message + // TODO: Handle API errors (address not found, ambiguous results, etc.) return await (await this.client.post(this.url + '/geocode', JSON.stringify({ address }))).data.elements; } /** - * - * @param id Se si sta cercando per coordinate, passare id = null + * Reverse geocoding: converts coordinates to address or looks up by ID + * @param type - Type of reverse lookup ('id', 'coordinates', or 'id | coordinates') + * @param id - Optional element ID (pass null when searching by coordinates) + * @param lat - Latitude coordinate + * @param long - Longitude coordinate */ async reverse(type: 'id' | 'coordinates' | 'id | coordinates', id = null, lat?: string, long?: string) { + // TODO: Validate that either id or coordinates (lat/long) are provided + // TODO: Add graceful error messages for invalid coordinates or ID not found const body = id ? { id, lat, long} : { lat, long } return await (await this.client.post(this.url + '/geocode', JSON.stringify(body))).data.elements; } + /** + * Gets the full service URL based on environment + */ get url() { return getBaseUrl(this.environment, this.baseUrl) } diff --git a/src/Services/Imprese.ts b/src/Services/Imprese.ts index 4f1c8ae..e9d7f1e 100644 --- a/src/Services/Imprese.ts +++ b/src/Services/Imprese.ts @@ -19,6 +19,9 @@ interface NaturaGiuridica { valore: string; } +/** + * Service for querying Italian business registry (companies) data + */ export class Imprese implements Service { client: AxiosInstance; readonly service = 'imprese'; @@ -30,53 +33,96 @@ export class Imprese implements Service { this.environment = environment; } + /** + * Gets basic company information by VAT number (Partita IVA) + * @param partitaIva - The Italian VAT number + */ async getByPartitaIva(partitaIva: string) { + // TODO: Validate partitaIva format and provide graceful error message for invalid or not found VAT return await (await this.client.get(this.url + '/base/' + partitaIva)).data.data; } + /** + * Gets advanced company information by VAT number + * @param partitaIva - The Italian VAT number + */ async getAdvancedByPartitaIva(partitaIva: string) { + // TODO: Validate partitaIva and handle errors for invalid VAT or insufficient permissions return await (await this.client.get(this.url + '/advance/' + partitaIva)).data.data; } + /** + * Checks if a company is closed/ceased operations + * @param partitaIva - The Italian VAT number + * @returns Boolean indicating if closed, or full response data + */ async isClosed(partitaIva: string): Promise { + // TODO: Add error handling for invalid partitaIva with user-friendly message const res = (await this.client.get(this.url + '/closed/' + partitaIva)).data.data return res.cessata ? res.cessata : res; } + /** + * Gets VAT group information for a company + * @param partitaIva - The Italian VAT number + */ async gruppoIva(partitaIva: string) { + // TODO: Handle cases where company is not in a VAT group with clear message return await (await this.client.get(this.url + '/gruppoiva/' + partitaIva)).data.data; } - + /** + * Gets the certified email (PEC) for a company + * @param partitaIva - The Italian VAT number + * @returns The PEC email address or full response data + */ async getPec(partitaIva: string) { + // TODO: Add graceful error message when PEC is not available or partitaIva is invalid const res = (await this.client.get(this.url + '/pec/' + partitaIva)).data.data return res.pec ? res.pec : res; } - + /** - * Autocomplete service - * Wildcards (*) can be used at the beginning or at the end of the string. + * Autocomplete service for company name search + * Wildcards (*) can be used at the beginning or end of the string + * @param query - Search query (automatically wrapped with wildcards if not present) */ async autocomplete(query: string): Promise { + // TODO: Validate non-empty query and provide helpful error message if (!query.match(/\*/)) query = `*${query}*` return await (await this.client.get(this.url + '/autocomplete/' + query)).data.data; } /** - * Richiede accesso ad /advance + * Advanced search for companies with multiple criteria + * Requires access to /advance endpoint + * @param searchQuery - Search criteria (denomination, province, VAT, fiscal code) */ async search(searchQuery: SearchImprese): Promise> { + // TODO: Validate search parameters and handle insufficient permissions with clear message return await (await this.client.get(this.url + '/advance', { params: searchQuery })).data.data; } + /** + * Lists all available legal forms (natura giuridica) + */ async listFormeGiuridiche(): Promise { + // TODO: Add error handling for API failures return await (await this.client.get(this.url + '/forma_giuridica')).data.data; } + /** + * Gets information about a specific legal form + * @param legalCode - The legal form code + */ async getFormaGiuridica(legalCode: string): Promise { + // TODO: Validate legalCode and provide graceful error message for invalid or not found codes return await (await this.client.get(this.url + '/forma_giuridica/' + legalCode)).data.data; } + /** + * Gets the full service URL based on environment + */ get url() { return getBaseUrl(this.environment, this.baseUrl) } diff --git a/src/Services/MarcheTemporali.ts b/src/Services/MarcheTemporali.ts index ebc51f5..d201881 100644 --- a/src/Services/MarcheTemporali.ts +++ b/src/Services/MarcheTemporali.ts @@ -18,6 +18,9 @@ export interface newMarca { password: string; } +/** + * Service for managing digital timestamps (Marche Temporali) + */ export class MarcheTemporali implements Service { client: AxiosInstance; readonly service = 'marcheTemporali'; @@ -27,23 +30,43 @@ export class MarcheTemporali implements Service { constructor(client: AxiosInstance, environment: Environment) { this.client = client; this.environment = environment; - } + } + /** + * Lists all purchased timestamp batches + */ async listMarche(): Promise> { + // TODO: Add error handling for API failures return await (await this.client.get(this.url + '/marche')).data.data; } + /** + * Purchases a new batch of timestamps + * @param type - Provider type ('infocert' or 'aruba') + * @param quantity - Number of timestamps to purchase + */ async purchase(type: 'infocert' | 'aruba', quantity: number): Promise { - if (quantity < 1) throw 'Quantity must be positive'; + if (quantity < 1) { + // TODO: Replace string throw with proper Error object + throw 'Quantity must be positive'; + } + // TODO: Handle API errors with user-friendly messages (insufficient funds, provider errors, etc.) return await (await this.client.get(this.url + '/marche/' + type + '/' + quantity)).data.data; } /** - * Controlla se una quantità minima di marche è diponibile + * Checks the availability of timestamps + * @param type - Provider type ('infocert' or 'aruba') + * @param amount - Minimum quantity to check + * @returns Number of available timestamps */ async checkAvailability(type: 'infocert' | 'aruba', amount: number): Promise { - if (amount < 1) throw 'Quantity must be positive'; - + if (amount < 1) { + // TODO: Replace string throw with proper Error object + throw 'Quantity must be positive'; + } + + // TODO: Add error handling for API failures let availability: string | number = await (await this.client.get(this.url + '/availability/' + type + '/' + amount)).data.data.availability; if (typeof availability === 'string') { availability = parseInt(availability); @@ -53,9 +76,13 @@ export class MarcheTemporali implements Service { } /** - * Controlla se una quantità minima di marche è diponibile + * Checks the status of a purchased timestamp batch + * @param username - Batch username + * @param password - Batch password + * @returns Available and used timestamp counts */ async checkLotto(username: string, password: string): Promise<{available: number, used: number}> { + // TODO: Validate credentials and add graceful error messages for invalid credentials let res = await (await this.client.post(this.url + '/check_lotto', JSON.stringify({username, password}))).data.data; for (const [key, value] of Object.entries(res)) { if (typeof value === 'string') { @@ -67,22 +94,43 @@ export class MarcheTemporali implements Service { } /** - * Controlla se una quantità minima di marche è diponibile, ritorna un booleano + * Checks if a minimum quantity of timestamps is available + * @param type - Provider type ('infocert' or 'aruba') + * @param amount - Minimum quantity required + * @returns True if available quantity meets or exceeds requirement */ async isAvailable(type: 'infocert' | 'aruba', amount: number): Promise { let availability: string | number = await this.checkAvailability(type, amount); return (availability >= amount); } + /** + * Applies a digital timestamp to a file + * @param username - Batch username + * @param password - Batch password + * @param file - Base64 encoded file content + * @param type - Provider type ('infocert' or 'aruba') + * @param mime - Whether to include MIME type (default: false) + */ async mark(username: string, password: string, file: string, type: 'infocert' | 'aruba' , mime = false) { + // TODO: Validate file content and credentials + // TODO: Add graceful error messages for invalid credentials or file format return await (await this.client.post(this.url + '/marca', JSON.stringify({username, password, file, mime, type}))).data.data; } + /** + * Analyzes an existing timestamped file + * @param file - Base64 encoded timestamped file + */ async analyze(file: string) { + // TODO: Validate file parameter and provide helpful error messages return await (await this.client.post(this.url + '/analisi', JSON.stringify({file}))).data.data; } + /** + * Gets the full service URL based on environment + */ get url() { return getBaseUrl(this.environment, this.baseUrl) } -} \ No newline at end of file +} diff --git a/src/Services/Pa.ts b/src/Services/Pa.ts index 0416da5..96d8430 100644 --- a/src/Services/Pa.ts +++ b/src/Services/Pa.ts @@ -2,6 +2,9 @@ import { AxiosInstance } from "axios"; import { Environment, Service, } from ".."; import { getBaseUrl } from "../utils"; +/** + * Service for querying Italian Public Administration (PA) entities + */ export class Pa implements Service { client: AxiosInstance; readonly service = 'pa'; @@ -13,11 +16,19 @@ export class Pa implements Service { this.environment = environment; } + /** + * Finds Public Administration entity by VAT number + * @param partitaIva - The VAT number (Partita IVA) of the PA entity + */ async findPa(partitaIva?: string) { + // TODO: Add validation for partitaIva and graceful error message for not found or invalid VAT return await (await this.client.get(this.url + '/' + partitaIva)).data.data; } + /** + * Gets the full service URL based on environment + */ get url() { return getBaseUrl(this.environment, this.baseUrl) } -} \ No newline at end of file +} diff --git a/src/Services/PecMassiva.ts b/src/Services/PecMassiva.ts index 175154b..c5e2bb0 100644 --- a/src/Services/PecMassiva.ts +++ b/src/Services/PecMassiva.ts @@ -10,6 +10,9 @@ export interface PecStatus { message?: string; } +/** + * Service for sending certified emails (PEC - Posta Elettronica Certificata) in bulk + */ export class PecMassiva implements Service { client: AxiosInstance; readonly service = 'pecMassiva'; @@ -21,16 +24,30 @@ export class PecMassiva implements Service { constructor(client: AxiosInstance, environment: Environment) { this.client = client; this.environment = environment; - } + } + /** + * Sets PEC account credentials for sending emails + * @param username - PEC account username + * @param password - PEC account password + */ setCredentials(username: string, password: string) { this.username = username; this.password = password; } + /** + * Retrieves all PEC emails associated with a code + * @param code - The tracking code + * @returns Array of PEC status objects + */ async getAll(code: string): Promise> { - if (!this.username || !this.password) throw 'Please set your credentials first'; - return await (await this.client.get(this.url + '/send/' + code, { + if (!this.username || !this.password) { + // TODO: Replace string throw with proper Error object + throw 'Please set your credentials first'; + } + // TODO: Add error handling for invalid code or API failures + return await (await this.client.get(this.url + '/send/' + code, { headers: { 'x-username': this.username, 'x-password': this.password, @@ -38,14 +55,27 @@ export class PecMassiva implements Service { })).data.data; } + /** + * Sends a certified email (PEC) + * @param sender - Sender PEC email address + * @param recipient - Recipient PEC email address + * @param body - Email body content + * @param subject - Email subject + * @param attachments - Optional array of attachments with name and file content + */ async send(sender: string, recipient: string, body: string, subject: string, attachments?: Array<{name: string, file: Buffer | any}>) { + // TODO: Validate email addresses format + // TODO: Add graceful error messages for invalid credentials, email format, or send failures const b: any = { sender, recipient, body, subject, username: this.username, password: this.password }; if (attachments) b.attachments = attachments; return await (await this.client.post(this.url + '/send', JSON.stringify(b))).data.data; } - + + /** + * Gets the full service URL based on environment + */ get url() { return getBaseUrl(this.environment, this.baseUrl) } -} \ No newline at end of file +} diff --git a/src/Services/Postontarget.ts b/src/Services/Postontarget.ts index cef5b3b..9829a28 100644 --- a/src/Services/Postontarget.ts +++ b/src/Services/Postontarget.ts @@ -19,6 +19,9 @@ interface SearchBody { micro_id?: number; } +/** + * Service for accessing targeted business mailing lists + */ export class Postontarget implements Service { client: AxiosInstance; readonly service = 'postontarget'; @@ -30,51 +33,106 @@ export class Postontarget implements Service { this.environment = environment; } + /** + * Gets available country filter options + * @param query - Country query filters + * @param limit - Result limit (default: 0 for no limit) + */ async getCountriesFields(query: CountryQuery, limit = 0) { + // TODO: Add validation and error handling return await (await this.client.post(this.url + '/fields/country', JSON.stringify({ query, limit }))).data.data; } + /** + * Gets available employee count filter options + * @param query - Filter query + * @param limit - Result limit (default: 0 for no limit) + */ async getDipendentiFields(query: any, limit = 0) { + // TODO: Add validation and error handling return await (await this.client.post(this.url + '/fields/dipendenti', JSON.stringify({ query, limit }))).data.data; } + /** + * Gets available revenue filter options + * @param query - Filter query + * @param limit - Result limit (default: 0 for no limit) + */ async getFatturatoFields(query: any, limit = 0) { + // TODO: Add validation and error handling return await (await this.client.post(this.url + '/fields/fatturato', JSON.stringify({ query, limit }))).data.data; } + /** + * Gets available legal form filter options + * @param query - Filter query + * @param limit - Result limit (default: 0 for no limit) + */ async getFormaGiuridicaFields(query: any, limit = 0) { + // TODO: Add validation and error handling return await (await this.client.post(this.url + '/fields/forma_giuridica', JSON.stringify({ query, limit }))).data.data; } + /** + * Gets available macro-category filter options + * @param query - Filter query + * @param limit - Result limit (default: 0 for no limit) + */ async getMacroCatFields(query: any, limit = 0) { + // TODO: Add validation and error handling return await (await this.client.post(this.url + '/fields/macrocategorie', JSON.stringify({ query, limit }))).data.data; } + /** + * Gets available micro-category filter options + * @param query - Filter query + * @param limit - Result limit (default: 0 for no limit) + */ async getMicroCatFields(query: any, limit = 0) { + // TODO: Add validation and error handling return await (await this.client.post(this.url + '/fields/microcategorie', JSON.stringify({ query, limit }))).data.data; } - // - + /** + * Searches for company records based on criteria + * @param query - Search criteria (country, employees, revenue, legal form, categories) + */ async findCompanyRecords(query: SearchBody) { + // TODO: Validate search parameters and provide helpful error messages return await (await this.client.post(this.url + '/search', JSON.stringify(query))).data.data; } + /** + * Purchases company records from a search result + * @param id_request - The search request ID + * @param records - Number of records to purchase + */ async buyCompanyRecords(id_request: string, records: number) { + // TODO: Add validation and graceful error messages for insufficient credits or invalid request ID return await (await this.client.post(this.url + '/search', JSON.stringify({ id_request, records }))).data.data; } - // + /** + * Lists all purchase requests + */ async listRequests(): Promise{ + // TODO: Add error handling for API failures return await (await this.client.get(this.url + '/state')).data.data } + /** + * Gets details of a specific purchase request + * @param id - The request ID + */ async getRequest(id: string) { + // TODO: Validate ID and add error handling for not found cases return await (await this.client.get(this.url + '/state/' + id)).data.data } - + /** + * Gets the full service URL based on environment + */ get url() { return getBaseUrl(this.environment, this.baseUrl) } -} \ No newline at end of file +} diff --git a/src/Services/Sms.ts b/src/Services/Sms.ts index 6e9457b..69c9396 100644 --- a/src/Services/Sms.ts +++ b/src/Services/Sms.ts @@ -17,6 +17,9 @@ export interface SmsRecipient { fields: any; } +/** + * Service for sending and managing SMS messages + */ export class Sms implements Service { client: AxiosInstance; readonly service = 'sms'; @@ -29,36 +32,50 @@ export class Sms implements Service { } /** - * @param id Se viene passato un id, ritorna un array contenente esclusivamente quel messaggio - * @returns Ritorna la lista di tutti i messaggi inviati + * Retrieves sent SMS messages + * @param id - Optional message ID to retrieve a specific message + * @returns Array of messages (all messages or single message if ID provided) */ async getMessages(id?: string): Promise> { - const query = id ? `${id}` : ''; + // TODO: Add error handling for invalid ID or API failures + const query = id ? \`\${id}\` : ''; return await (await this.client.get(this.url + '/messages/' + query)).data.data; } /** - * - * @param recipients Phone number of the recipient and wanting the 'fields' object in which to insert the parameters that we want to enter in the 'body' like this: {'number':'+39-34xxxxx987', 'fields':{'name':'simone', 'surname':'rossi'}}. Mandatory is the international prefix which must be separated from the rest by '-' like this: '+39-number'. Any other format will be considered bad recipients and placed among the invalid - * @todo Supporto per le transazioni + * Sends SMS messages to one or more recipients + * @param sender - Sender name/number (3-12 chars for string, 3-14 for number) + * @param message - SMS text body (supports placeholders with recipient fields) + * @param recipients - Phone number(s) with international prefix (format: '+39-number') + * @param priority - Message priority (default: 1) + * @param options - Additional options (flash, scheduled send, callbacks, etc.) + * @param test - Whether this is a test message (default: false) + * @todo Add support for transactions */ - async send(sender: string | number, message: string, recipients: string | Array, + async send(sender: string | number, message: string, recipients: string | Array, priority: number = 1, options: SmsOptions, test: boolean = false, ): Promise> { if (typeof sender === 'string' && (sender.length > 12 || sender.length < 3)) { + // TODO: Replace string throw with proper Error object for better error handling throw 'sender length must be less than 12 chars and more than 3' } if (typeof sender === 'number' && (sender.toString().length > 14 || sender.toString().length < 3)) { + // TODO: Replace string throw with proper Error object for better error handling throw 'sender number length must be less than 14 chars and more than 3' } + // TODO: Add validation for recipients format and provide helpful error messages + // TODO: Handle API errors (invalid recipients, insufficient credits, etc.) with user-friendly messages return await (await this.client.post(this.url + '/messages/', JSON.stringify({ - test, sender, recipients, body: message, transaction: false, priority + test, sender, recipients, body: message, transaction: false, priority }))).data.data; } + /** + * Gets the full service URL based on environment + */ get url() { return getBaseUrl(this.environment, this.baseUrl) } -} \ No newline at end of file +} diff --git a/src/Services/SplitPayment.ts b/src/Services/SplitPayment.ts index 94d1d3a..b8a7856 100644 --- a/src/Services/SplitPayment.ts +++ b/src/Services/SplitPayment.ts @@ -10,6 +10,9 @@ interface Company { update_timestamp?: number; } +/** + * Service for checking Italian companies subject to split payment (scissione dei pagamenti) + */ export class Splitpayment implements Service { client: AxiosInstance; readonly service = 'splitpayment'; @@ -21,14 +24,26 @@ export class Splitpayment implements Service { this.environment = environment; } + /** + * Lists all companies subject to split payment + */ async listCompanies(): Promise> { + // TODO: Add error handling for API failures return await (await this.client.get(this.url + '/')).data.data; } + /** + * Checks if a specific company is subject to split payment + * @param cf - The fiscal code (Codice Fiscale) of the company + */ async company(cf?: string) { + // TODO: Validate fiscal code format and provide graceful error message for not found cases return await (await this.client.get(this.url + '/' + cf)).data.data; } + /** + * Gets the full service URL based on environment + */ get url() { return getBaseUrl(this.environment, this.baseUrl) } diff --git a/src/Services/UfficioPostale.ts b/src/Services/UfficioPostale.ts index d4961d6..1e0ecca 100644 --- a/src/Services/UfficioPostale.ts +++ b/src/Services/UfficioPostale.ts @@ -129,6 +129,9 @@ interface TrackingStatus { definitivo: boolean; } +/** + * Service for sending physical mail (registered letters, telegrams, ordinary mail) via Italian Post + */ export class UfficioPostale implements Service { client: AxiosInstance; readonly service = 'ufficioPostale'; @@ -140,109 +143,228 @@ export class UfficioPostale implements Service { this.environment = environment; } + /** + * Lists available street designation types (DUG - Denominazione Urbanistica Generica) + */ async listDug(): Promise<{codice_dug: string; dug: string}[]> { + // TODO: Add error handling for API failures return await (await this.client.get(this.url + '/dug/')).data.data; } + /** + * Searches for validated addresses + * @param cap - Postal code (CAP) + * @param comune - Municipality name + * @param dug - Street designation type + */ async addresses(cap: string, comune: string, dug: string) { + // TODO: Validate parameters and add graceful error messages for invalid or not found addresses return await (await this.client.get(this.url + '/indirizzi', { params: {cap, comune, dug}})).data.data; } + /** + * Gets pricing information for postal services + */ async pricing(): Promise> { + // TODO: Add error handling for API failures return await (await this.client.get(this.url + '/pricing/')).data.data; } + /** + * Tracks a postal shipment + * @param id - The tracking ID + * @returns Array of tracking status updates + */ async track(id: string): Promise { + // TODO: Validate ID and add graceful error messages for invalid or not found tracking IDs return await (await this.client.get(this.url + '/tracking/' + id)).data.data; } + /** + * Gets municipalities for a postal code + * @param postalCode - The postal code (CAP) + */ async comuni(postalCode: string) { + // TODO: Validate postal code format and handle not found cases return await (await this.client.get(this.url + '/comuni/' + postalCode)).data.data; } - /** - * Raccomandate - */ + // Registered mail (Raccomandate) methods + /** + * Lists all registered mail items + */ async listRaccomandate(): Promise { + // TODO: Add error handling for API failures return await (await this.client.get(this.url + '/raccomandate/')).data.data; } + /** + * Gets details of a specific registered mail item + * @param id - The registered mail ID + */ async getRaccomandata(id: string) { + // TODO: Validate ID and add error handling for not found cases return await (await this.client.get(this.url + '/raccomandate/' + id)).data.data; } + /** + * Creates a new registered mail item + * @param mittente - Sender information + * @param destinatari - Recipient(s) information + * @param documento - Base64 encoded document(s) + * @param autoconfirm - Auto-confirm and send immediately (default: true) + * @param options - Additional options (double-sided, color, return receipt) + */ async createRaccomandata(mittente: Mittente, destinatari: Destinatario[], documento: string[], autoconfirm = true, options: OpzioniRaccomandata = {}): Promise { + // TODO: Validate sender and recipient data + // TODO: Handle API errors (invalid addresses, document format errors, insufficient credits, etc.) if (!Array.isArray(destinatari)) destinatari = [destinatari]; return await (await this.client.post(this.url + '/raccomandate/', JSON.stringify({ mittente, destinatari, documento, opzioni: { autoconfirm, ...options } }))).data.data; } + /** + * Confirms a registered mail item for sending + * @param id - The registered mail ID + */ async confirmRaccomandata(id: string) { + // TODO: Validate ID and handle errors for already confirmed or invalid items return await (await this.client.patch(this.url + '/raccomandate/' + id, JSON.stringify({ confirmed: true }))).data.data; } + // Telegram methods + /** - * Telegrammi - */ + * Lists all telegrams + */ async listTelegrammi(): Promise { + // TODO: Add error handling for API failures return await (await this.client.get(this.url + '/telegrammi/')).data.data; } + /** + * Gets details of a specific telegram + * @param id - The telegram ID + */ async getTelegramma(id: string) { + // TODO: Validate ID and add error handling for not found cases return await (await this.client.get(this.url + '/telegrammi/' + id)).data.data; } + /** + * Creates a new telegram + * @param mittente - Sender information + * @param destinatari - Recipient(s) information + * @param documento - Base64 encoded document content + * @param autoconfirm - Auto-confirm and send immediately (default: true) + * @param options - Additional options + */ async createTelegramma(mittente: Mittente, destinatari: Destinatario[], documento: string, autoconfirm = true, options: OpzioniTelegramma = {}): Promise { + // TODO: Validate sender and recipient data + // TODO: Handle API errors (invalid data, document format errors, etc.) if (!Array.isArray(destinatari)) destinatari = [destinatari]; return await (await this.client.post(this.url + '/telegrammi/', JSON.stringify({ mittente, destinatari, documento, opzioni: { autoconfirm, ...options } }))).data.data; } + /** + * Confirms a telegram for sending + * @param id - The telegram ID + */ async confirmTelegramma(id: string) { + // TODO: Validate ID and handle errors for already confirmed or invalid items return await (await this.client.patch(this.url + '/telegrammi/' + id, JSON.stringify({ confirmed: true }))).data.data; } + // Ordinary mail methods + /** - * Posta ordinaria - */ + * Lists all ordinary mail items + */ async listOrdinarie() { + // TODO: Add error handling for API failures return await (await this.client.get(this.url + '/ordinarie/')).data.data; } + /** + * Gets details of a specific ordinary mail item + * @param id - The ordinary mail ID + */ async getOrdinaria(id: string) { + // TODO: Validate ID and add error handling for not found cases return await (await this.client.get(this.url + '/ordinarie/' + id)).data.data; } + /** + * Creates a new ordinary mail item + * @param mittente - Sender information + * @param destinatari - Recipient(s) information + * @param documento - Base64 encoded document + * @param autoconfirm - Auto-confirm and send immediately (default: true) + * @param options - Additional options + */ async createOrdinaria(mittente: Mittente, destinatari: Destinatario[], documento: string, autoconfirm = true, options: OpzioniLOL = {}): Promise { + // TODO: Validate sender and recipient data + // TODO: Handle API errors (invalid data, document format errors, etc.) if (!Array.isArray(destinatari)) destinatari = [destinatari]; return await (await this.client.post(this.url + '/ordinarie/', JSON.stringify({ mittente, destinatari, documento, opzioni: { autoconfirm, ...options } }))).data.data; } + /** + * Confirms an ordinary mail item for sending + * @param id - The ordinary mail ID + */ async confirmOrdinaria(id: string) { + // TODO: Validate ID and handle errors for already confirmed or invalid items return await (await this.client.patch(this.url + '/ordinarie/' + id, JSON.stringify({ confirmed: true }))).data.data; - } + } + + // Priority mail methods /** - * Posta prioritaria + * Lists all priority mail items */ - async listPrioritarie() { + // TODO: Add error handling for API failures return await (await this.client.get(this.url + '/prioritarie/')).data.data; } + /** + * Gets details of a specific priority mail item + * @param id - The priority mail ID + */ async getPrioritaria(id: string) { + // TODO: Validate ID and add error handling for not found cases return await (await this.client.get(this.url + '/prioritarie/' + id)).data.data; } + /** + * Creates a new priority mail item + * @param mittente - Sender information + * @param destinatari - Recipient(s) information + * @param documento - Base64 encoded document + * @param autoconfirm - Auto-confirm and send immediately (default: true) + * @param options - Additional options + */ async createPrioritaria(mittente: Mittente, destinatari: Destinatario[], documento: string, autoconfirm = true, options: OpzioniLOL = {}): Promise { + // TODO: Validate sender and recipient data + // TODO: Handle API errors (invalid data, document format errors, etc.) if (!Array.isArray(destinatari)) destinatari = [destinatari]; return await (await this.client.post(this.url + '/prioritarie/', JSON.stringify({ mittente, destinatari, documento, opzioni: { autoconfirm, ...options } }))).data.data; } + /** + * Confirms a priority mail item for sending + * @param id - The priority mail ID + */ async confirmPrioritaria(id: string) { + // TODO: Validate ID and handle errors for already confirmed or invalid items return await (await this.client.patch(this.url + '/prioritarie/' + id, JSON.stringify({ confirmed: true }))).data.data; - } + } + /** + * Gets the full service URL based on environment + */ get url() { return getBaseUrl(this.environment, this.baseUrl) } -} \ No newline at end of file +} diff --git a/src/Services/Valutometro.ts b/src/Services/Valutometro.ts index bab4963..c5402f6 100644 --- a/src/Services/Valutometro.ts +++ b/src/Services/Valutometro.ts @@ -46,6 +46,9 @@ interface Coordinate { lng?: number; } +/** + * Service for real estate property valuation in Italy + */ export class Valutometro implements Service { client: AxiosInstance; readonly service = 'valutometro'; @@ -55,49 +58,95 @@ export class Valutometro implements Service { constructor(client: AxiosInstance, environment: Environment) { this.client = client; this.environment = environment; - } + } + /** + * Internal method to retrieve property types + * @param id - Optional property type ID + */ async immobili(id?: string) { + // TODO: Add error handling for API failures return await (await this.client.get(this.url + '/immobili' + (id ? `/${id}` : ''))).data.data; } + /** + * Lists all available property types + */ async listPropertyTypes(): Promise> { return this.immobili(); } + /** + * Gets details of a specific property type + * @param id - The property type ID + */ async getProperty(id: string): Promise { + // TODO: Validate ID and add graceful error message for not found cases return this.immobili(id); } + /** + * Internal method to retrieve contract types + * @param id - Optional contract type ID + */ async contratti(id?: string) { + // TODO: Add error handling for API failures return await (await this.client.get(this.url + '/contratti' + (id ? `/${id}` : ''))).data.data; } + /** + * Lists all available contract types (sale, rent, etc.) + */ async listContractTypes(): Promise> { return this.contratti(); } + /** + * Gets details of a specific contract type + * @param id - The contract type ID + */ async getContract(id: string): Promise { + // TODO: Validate ID and add graceful error message for not found cases return this.contratti(id); } + /** + * Gets a property valuation quote by address + * @param indirizzo - The property address + * @param tipo_immobile - Property type ID + * @param tipo_contratto - Contract type ID + */ async quote(indirizzo: string, tipo_immobile: string, tipo_contratto: string): Promise { + // TODO: Validate parameters and provide helpful error messages for invalid address or types return (await this.client.post(this.url + '/quotazione', JSON.stringify({ indirizzo, tipo_contratto, tipo_immobile }))).data.data; } + /** + * Gets address validation and additional parameters for valuation + * @param indirizzo - The property address + * @param tipo_immobile - Property type ID + * @param tipo_contratto - Contract type ID + */ async addressQuotation(indirizzo: string, tipo_immobile: string, tipo_contratto: string): Promise { + // TODO: Add validation and error handling for invalid addresses return (await this.client.post(this.url + '/indirizzo', JSON.stringify({ indirizzo, tipo_contratto, tipo_immobile }))).data.data; } /** - * @param searchParams i paramentri aggiuntivi che possono essere ricavati chiamando prima `addressQuotation` + * Performs a detailed property valuation + * @param univoco - Unique identifier from previous address quotation + * @param searchParams - Additional parameters obtained from addressQuotation */ async valuation(univoco: string, searchParams = {}) { + // TODO: Validate univoco parameter and provide clear error messages return (await this.client.post(this.url + '/valutazione', JSON.stringify({ univoco, ...searchParams }))).data.data; } + /** + * Gets the full service URL based on environment + */ get url() { return getBaseUrl(this.environment, this.baseUrl) } -} \ No newline at end of file +} diff --git a/src/Services/Visengine.ts b/src/Services/Visengine.ts index 457887f..3e743e6 100644 --- a/src/Services/Visengine.ts +++ b/src/Services/Visengine.ts @@ -60,6 +60,9 @@ interface RichiestaDocumento { export type Transaction = 'open' | 'close'; +/** + * Service for requesting official documents and reports (visure) from Italian registries + */ export class Visengine implements Service { client: AxiosInstance; readonly service = 'visengine'; @@ -70,53 +73,79 @@ export class Visengine implements Service { constructor(client: AxiosInstance, environment: Environment) { this.client = client; this.environment = environment; - } + } + /** + * Lists all available document types (visure) + */ async listServices(): Promise { + // TODO: Add error handling for API failures return await (await this.client.get(this.url + '/visure')).data.data; } + /** + * Gets detailed description and requirements for a specific document type + * @param hash - The document type identifier hash + */ async getServiceDescription(hash: string): Promise { + // TODO: Validate hash and add error handling for not found cases const res = await (await this.client.get(this.url + '/visure/' + hash)).data.data; if (res.json_struttura.istruzioni) { res.json_struttura.istruzioni = Buffer.from(res.json_struttura.istruzioni, 'base64').toString(); } - + return res; } + /** + * Lists all document requests made by the user + */ async listRequests(): Promise { + // TODO: Add error handling for API failures return await (await this.client.get(this.url + '/richiesta')).data.data; } + /** + * Gets details of a specific document request + * @param id - The request ID + */ async getRequest(id: string) { + // TODO: Validate ID and add error handling for not found cases return await (await this.client.get(this.url + '/richiesta/' + id)).data.data; } /** - * - * @param options the options of a specific visura - * @param callback optional: a callback object to ping when the visura is ready - * @param transaction whether to close the request now or in the future with an optional PUT request - * @returns + * Creates a new document request + * @param hash - Document type hash + * @param json_visura - Document data fields as required by the specific document type + * @param options - Optional additional options + * @param callback - Optional callback configuration for request completion notification + * @param email_target - Optional email address to send the completed document + * @param transaction - Transaction mode: 'close' to process immediately, 'open' to process later + * @param test - Whether this is a test request (default: false) */ async createRequest(hash: string, json_visura: any, options?: any, callback?: Callback, email_target?: string, transaction: Transaction = 'close', test = false) { + // TODO: Validate required parameters and provide clear error messages + // TODO: Handle API errors (invalid hash, missing required fields, insufficient credits, etc.) const state = this.getTransactionStatus(transaction); let body: {[key:string]: any} = { hash_visura: hash, json_visura, email_target, state, test }; if (callback) body.callback = callback; if (email_target) body.email_target = email_target; if (options) body.opzioni = options; - + this.lastVisura = await (await this.client.post(this.url + '/richiesta', JSON.stringify(body))).data.data; return this.lastVisura; } /** - * - * @param updatedFields un oggetto chiave-valore dei campi della visura che si vogliono aggiornare: - * `{'$0': ...nuovi_valori, '$3': ...nuovi_valori}` + * Updates an existing open document request + * @param id - The request ID + * @param updatedFields - Key-value object of fields to update (e.g., {'$0': ...new_values}) + * @param transaction - Transaction mode: 'close' to process, 'open' to keep open */ async updateRequest(id: string, updatedFields: {[key:string]: any}, transaction: Transaction = 'close' ) { + // TODO: Validate ID and updatedFields parameters + // TODO: Add graceful error messages for invalid request ID or field validation errors let newFields: {[key:string]: any} = {}; for (const [key, value] of Object.entries(updatedFields)) { newFields[`json_visura.${key}`] = value; @@ -126,16 +155,29 @@ export class Visengine implements Service { return await (await this.client.put(this.url + '/richiesta/' + id, JSON.stringify(body))).data.data; } + /** + * Downloads the completed document + * @param id - The request ID + * @returns Document details including Base64 encoded file content + */ async getDocument(id: string): Promise { + // TODO: Validate ID and handle cases where document is not ready yet return await (await this.client.get(this.url + '/richiesta/' + id)).data.data; } + /** + * Gets the full service URL based on environment + */ get url() { return getBaseUrl(this.environment, this.baseUrl) } + /** + * Converts transaction type to numeric state value + * @param t - Transaction type ('close' or 'open') + */ getTransactionStatus(t: Transaction) { return t === 'close' ? 0 : 1 } -} \ No newline at end of file +} diff --git a/src/index.ts b/src/index.ts index 91d974b..1cfec62 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,12 +19,15 @@ import { UfficioPostale } from "./Services/UfficioPostale"; export type Environment = 'test'| 'production'; export type ValidHttpMethod = 'GET' |'POST' | 'UPDATE' | 'PATCH' | 'DELETE' | '*'; + +// Represents a scope object for API authorization export interface ScopeObject { domain: string; method: string; mode: ValidHttpMethod; } +// Base interface for all OpenAPI services export interface Service { client: AxiosInstance; readonly service: string; @@ -32,6 +35,9 @@ export interface Service { environment: Environment; } +/** + * Main OpenAPI client class for authenticating and accessing various services + */ class OpenApi { client?: AxiosInstance; environment: Environment; @@ -63,19 +69,20 @@ class OpenApi { } /** - * Crea il client di connessione con OpenApi - * Se l'autoRenew è attivo, controllerá lo stato del token - * prima di istanziare il client, ed in caso lo rinnoverà + * Creates the OpenAPI connection client + * If autoRenew is enabled, checks token status and renews if needed + * @param token - The authentication token + * @param autoRenew - Whether to automatically renew expiring tokens (default: true) */ async createClient(token: string, autoRenew = true) { this.token = token; - + try { - const tokenData = await axios.get(this.getOauthUrl() + '/token/' + token, { + const tokenData = await axios.get(this.getOauthUrl() + '/token/' + token, { auth: { username: this.username, password: this.apiKey } }); - + if (tokenData.status === 200 ) { const scopes: Array = tokenData.data.data[0].scopes; scopes.forEach(scope => { @@ -83,15 +90,18 @@ class OpenApi { this.scopes.push({ mode: scope.split(':', 1), domain: url[0], method: url[1] }); }) + // Auto-renew token if it expires within 15 days if (autoRenew && tokenData.data.data[0].expire < ((Math.floor(Date.now() / 1000) + (86400 * 15)))) { await this.renewToken(this.token); } } else if (tokenData.status === 204) { + // TODO: Replace string throw with proper Error object for better error handling throw 'The provided token does not exists or it was deleted' } } catch (err) { + // TODO: Add graceful error message with details about the failure (network, auth, invalid token, etc.) throw err; } @@ -99,12 +109,14 @@ class OpenApi { headers: { 'Authorization': 'Bearer ' + this.token } }); - [Comuni, Sms, Imprese, Geocoding, Pa, FirmaDigitale, MarcheTemporali, PecMassiva, + // Initialize all available services based on token scopes + [Comuni, Sms, Imprese, Geocoding, Pa, FirmaDigitale, MarcheTemporali, PecMassiva, Valutometro, Splitpayment, EuropeanVat, Visengine, Postontarget, Domains, UfficioPostale ].forEach(service => { //@ts-ignore const s = new service(this.client, this.environment); - + + // Only add service if it's included in the token scopes if (isServiceInScopes(this.scopes, s.baseUrl)) { //@ts-ignore this[s.service] = s; @@ -114,26 +126,36 @@ class OpenApi { return this; } + /** + * Renews an existing token with a new expiration time (1 year from now) + * @param token - The token to renew + */ async renewToken(token: string) { + // TODO: Add error handling for failed renewal attempts return await axios.patch(this.getOauthUrl() + '/token/' + token, { expire: 31536000 + Math.round(Date.now() / 1000) }, { auth: { username: this.username, password: this.apiKey } }); } /** - * Genera un token - * @param expire valore in giorni alla di scadenza del token, default: un anno + * Generates a new authentication token with specified scopes + * @param scopes - Service scopes (string or array of strings) + * @param expire - Token expiration in days (default: 365) + * @param autoRenew - Whether to enable auto-renewal (default: true) + * @returns The generated token string */ async generateToken(scopes: string | Array, expire: number = 365, autoRenew = true): Promise { + // Normalize scopes to array if (typeof scopes === 'string') { scopes = [scopes]; } + // Format scopes with proper prefix and wildcards scopes = scopes.map(scope => { if (!scope.match(/:/)) { scope = '*:' + scope.replace('/', '') + '/*'; } - + const url = OpenApi.splitScope(scope); return `${scope.split(':', 1)}:${this.prefix}${url[0].replace(/^test.|dev./, '')}/${url[1]}` }); @@ -148,25 +170,44 @@ class OpenApi { return res.data.token; } catch(err) { + // TODO: Add graceful error message indicating token generation failure with specific reason throw err; } } + /** + * Gets the OAuth service URL based on environment + */ getOauthUrl() { return 'https://'+ this.prefix +'oauth.altravia.com'; } + /** + * Returns environment prefix for URLs (empty for production, 'test.' for test) + */ get prefix() { return this.environment === 'test' ? 'test.': ''; } + /** + * Splits a scope string into domain and method parts + * @param scope - The scope string to split (format: "method:domain/path") + */ static splitScope(scope: string) { return scope.split(':', 2)[1].split('/', 2); } + /** + * Factory method to initialize and create an OpenAPI client instance + * @param environment - The environment to use ('test' or 'production') + * @param username - The API username + * @param apiKey - The API key + * @param token - Optional existing token to use + * @param autoRenew - Whether to enable auto-renewal (default: true) + */ static async init(environment: Environment, username: string, apiKey: string, token?: string, autoRenew = true) { const openapi = new OpenApi(environment, username, apiKey); - + if (token) { await openapi.createClient(token, autoRenew); } diff --git a/src/utils/index.ts b/src/utils/index.ts index bd5f0ad..db856be 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,11 +1,24 @@ import { ScopeObject } from ".."; +/** + * Checks if a service is authorized within the provided scopes + * @param scopes - Array of scope objects to check + * @param baseUrl - The base URL of the service to verify + * @returns The matching scope object if found, undefined otherwise + */ export function isServiceInScopes(scopes: Array, baseUrl: string) { + // TODO: Add validation for empty scopes array and provide graceful error message return scopes.find(scope => { return scope.domain.match(baseUrl); }); } +/** + * Constructs the full base URL for a service based on environment + * @param env - The environment ('test' or 'production') + * @param baseUrl - The base service URL + * @returns The complete HTTPS URL with environment prefix if applicable + */ export function getBaseUrl(env: string, baseUrl: string) { return 'https://' + ((env === 'test' ) ? 'test.' : '') + baseUrl; } \ No newline at end of file