All files / src/HandleProvider KoraLabsHandleProvider.ts

90.9% Statements 30/33
72.72% Branches 8/11
100% Functions 7/7
96.29% Lines 26/27

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104  19x                         19x     19x                     19x 3x 3x                     3x   3x               19x         2x       2x         7x 8x 8x   3x   5x 5x 5x 4x   4x                     8x       2x 2x 1x   1x         1x      
// cSpell:ignore kora koralabs
import {
  Asset,
  Cardano,
  HandleProvider,
  HandleResolution,
  HealthCheckResponse,
  ProviderError,
  ProviderFailure,
  ResolveHandlesArgs
} from '@cardano-sdk/core';
 
// eslint-disable-next-line import/no-extraneous-dependencies
import { IHandle } from '@koralabs/handles-public-api-interfaces';
import axios, { AxiosAdapter, AxiosInstance } from 'axios';
 
/** The KoraLabsHandleProvider endpoint paths. */
const paths = {
  handles: '/handles',
  healthCheck: '/health'
};
 
export interface KoraLabsHandleProviderDeps {
  serverUrl: string;
  adapter?: AxiosAdapter;
  policyId: Cardano.PolicyId;
}
 
export const toHandleResolution = ({ apiResponse, policyId }: { apiResponse: IHandle; policyId: Cardano.PolicyId }) => {
  const cardano = Cardano.PaymentAddress(apiResponse.resolved_addresses.ada);
  const result: HandleResolution = {
    addresses: { cardano },
    backgroundImage: apiResponse.bg_image ? Asset.Uri(apiResponse.bg_image) : undefined,
    cardanoAddress: cardano,
    handle: apiResponse.name,
    hasDatum: apiResponse.has_datum,
    image: apiResponse.image ? Asset.Uri(apiResponse.image) : undefined,
    policyId,
    profilePic: apiResponse.pfp_image ? Asset.Uri(apiResponse.pfp_image) : undefined
  };
 
  if ('btc' in apiResponse.resolved_addresses) result.addresses.bitcoin = apiResponse.resolved_addresses.btc;
 
  return result;
};
 
/**
 * Creates a KoraLabs Provider instance to resolve Standard Handles
 *
 * @param KoraLabsHandleProviderDeps The configuration object fot the KoraLabs Handle Provider.
 */
export class KoraLabsHandleProvider implements HandleProvider {
  private axiosClient: AxiosInstance;
  policyId: Cardano.PolicyId;
 
  constructor({ serverUrl, adapter, policyId }: KoraLabsHandleProviderDeps) {
    this.axiosClient = axios.create({
      adapter,
      baseURL: serverUrl
    });
    this.policyId = policyId;
  }
 
  resolveHandles({ handles }: ResolveHandlesArgs): Promise<Array<HandleResolution | null>> {
    // eslint-disable-next-line unicorn/consistent-function-scoping
    const resolveHandle = async (handle: string) => {
      try {
        const { data } = await this.axiosClient.get<IHandle>(`${paths.handles}/${handle}`);
 
        return toHandleResolution({ apiResponse: data, policyId: this.policyId });
      } catch (error) {
        Iif (error instanceof ProviderError) throw error;
        if (axios.isAxiosError(error)) {
          if (error.response?.status === 404) return null;
          Iif (error.request) throw new ProviderError(ProviderFailure.ConnectionFailure, error, error.code);
 
          throw new ProviderError(
            ProviderFailure.Unhealthy,
            error,
            `Failed to resolve handles due to: ${error.message}`
          );
        }
 
        throw new ProviderError(ProviderFailure.Unknown, error, 'Failed to resolve handles');
      }
    };
 
    return Promise.all(handles.map((handle) => resolveHandle(handle)));
  }
 
  async healthCheck(): Promise<HealthCheckResponse> {
    try {
      await this.axiosClient.get(`${paths.healthCheck}`);
      return { ok: true };
    } catch {
      return { ok: false };
    }
  }
 
  async getPolicyIds(): Promise<Cardano.PolicyId[]> {
    return [this.policyId];
  }
}