All files / src/HandleProvider KoraLabsHandleProvider.ts

89.65% Statements 26/29
70% Branches 7/10
100% Functions 7/7
95.83% Lines 23/24

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  19x                         19x     19x                     19x           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;
}): HandleResolution => ({
  backgroundImage: apiResponse.bg_image ? Asset.Uri(apiResponse.bg_image) : undefined,
  cardanoAddress: Cardano.PaymentAddress(apiResponse.resolved_addresses.ada),
  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
});
 
/**
 * 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];
  }
}