Skip to content

Commit

Permalink
Refactor: Move Everything Around
Browse files Browse the repository at this point in the history
Put interfaces and their implementations into folders based on their purpose: db, registry, current, loader.
  • Loading branch information
cmoesel committed Apr 7, 2024
1 parent ed72ce4 commit 25fba29
Show file tree
Hide file tree
Showing 24 changed files with 219 additions and 174 deletions.
96 changes: 0 additions & 96 deletions src/RegistryClient.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
import { downloadPackageTarballToCache } from './download';
import { LogFunction } from './utils';
import { axiosGet } from './utils/axiosUtils';
import { downloadPackageTarballToCache } from '../download';
import { LogFunction, axiosGet } from '../utils';
import { CurrentBuildClient, CurrentBuildClientOptions } from './CurrentBuildClient';

export type CurrentBuildClientOptions = {
log?: LogFunction;
};

export interface CurrentBuildClient {
downloadCurrentBuild(name: string, branch: string | null, cachePath: string): Promise<string>;
getCurrentBuildDate(name: string, branch?: string): Promise<string>;
}

export class BuildDotFHIRClient implements CurrentBuildClient {
export class BuildDotFhirDotOrgClient implements CurrentBuildClient {
private log: LogFunction;
constructor(options: CurrentBuildClientOptions = {}) {
this.log = options.log ?? (() => {});
Expand Down
10 changes: 10 additions & 0 deletions src/current/CurrentBuildClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { LogFunction } from '../utils';

export type CurrentBuildClientOptions = {
log?: LogFunction;
};

export interface CurrentBuildClient {
downloadCurrentBuild(name: string, branch: string | null, cachePath: string): Promise<string>;
getCurrentBuildDate(name: string, branch?: string): Promise<string>;
}
2 changes: 2 additions & 0 deletions src/current/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './CurrentBuildClient';
export * from './BuildDotFhirDotOrgClient';
11 changes: 11 additions & 0 deletions src/db/PackageDB.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { PackageInfo, PackageStats, ResourceInfo } from '../package';

export interface PackageDB {
clear(): void;
savePackageInfo(info: PackageInfo): void;
saveResourceInfo(info: ResourceInfo): void;
findPackageInfo(name: string, version: string): PackageInfo | undefined;
findResourceInfos(key: string): ResourceInfo[];
findResourceInfo(key: string): ResourceInfo | undefined;
getPackageStats(name: string, version: string): PackageStats | undefined;
}
19 changes: 9 additions & 10 deletions src/PackageDB.ts → src/db/SQLJSPackageDB.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import util from 'util';
import { Database, Statement } from 'sql.js';
import { PackageStats } from './PackageStats';
import { PackageInfo } from './PackageInfo';
import { ResourceInfo } from './ResourceInfo';
import util from 'util';
import { PackageInfo, PackageStats, ResourceInfo } from '../package';
import { PackageDB } from './PackageDB';

const CREATE_PACKAGE_TABLE =
'CREATE TABLE package (rowId INTEGER PRIMARY KEY, name char, version char, packagePath char, packageJSONPath char);';
Expand Down Expand Up @@ -30,7 +29,7 @@ const INSERT_RESOURCE = `INSERT INTO resource (${RESOURCE_PROPERTIES.join(
)}) VALUES (${RESOURCE_PROPERTIES.map(p => `:${p}`).join(', ')})`;
const FIND_PACKAGE = 'SELECT * FROM package WHERE name = :name and version = :version LIMIT 1';

export class PackageDB {
export class SQLJSPackageDB implements PackageDB {
private insertPackageStmt: Statement;
private insertResourceStmt: Statement;
private findPackageStmt: Statement;
Expand All @@ -49,7 +48,7 @@ export class PackageDB {
this.db.exec('VACUUM');
}

savePackageInfo(info: PackageInfo) {
savePackageInfo(info: PackageInfo): void {
const binding: any = {
':name': info.name,
':version': info.version
Expand All @@ -63,7 +62,7 @@ export class PackageDB {
this.insertPackageStmt.run(binding);
}

saveResourceInfo(info: ResourceInfo) {
saveResourceInfo(info: ResourceInfo): void {
const binding: any = {
':resourceType': info.resourceType
};
Expand Down Expand Up @@ -103,7 +102,7 @@ export class PackageDB {
this.insertResourceStmt.run(binding);
}

findPackageInfo(name: string, version: string): PackageInfo {
findPackageInfo(name: string, version: string): PackageInfo | undefined {
try {
this.findPackageStmt.bind({ ':name': name, ':version': version });
if (this.findPackageStmt.step()) {
Expand All @@ -128,15 +127,15 @@ export class PackageDB {
return results;
}

findResourceInfo(key: string): ResourceInfo {
findResourceInfo(key: string): ResourceInfo | undefined {
// TODO: Make this more sophisticate if/when it makes sense
const results = this.findResourceInfos(key);
if (results.length > 0) {
return results[0];
}
}

getPackageStats(name: string, version: string): PackageStats {
getPackageStats(name: string, version: string): PackageStats | undefined {
const pkg = this.findPackageInfo(name, version);
if (pkg == null) {
return;
Expand Down
2 changes: 2 additions & 0 deletions src/db/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './PackageDB';
export * from './SQLJSPackageDB';
2 changes: 2 additions & 0 deletions src/errors/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export * from './CurrentPackageLoadError';
export * from './IncorrectWildcardVersionFormatError';
export * from './InvalidPackageError';
export * from './InvalidResourceError';
export * from './LatestVersionUnavailableError';
export * from './PackageLoadError';
35 changes: 34 additions & 1 deletion src/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
import { FHIRDefinitions } from './FHIRDefinitions';
import { LogFunction } from './utils';
import { axiosGet } from './utils/axiosUtils';
import { getCustomRegistry, getDistUrl } from './utils/customRegistry';
import { AxiosResponse } from 'axios';
import { LatestVersionUnavailableError } from './errors/LatestVersionUnavailableError';

Expand Down Expand Up @@ -455,3 +454,37 @@ function formatDate(date: string): string {
? date.replace(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/, '$1-$2-$3T$4:$5:$6')
: '';
}

let hasLoggedCustomRegistry = false;

export function getCustomRegistry(log: LogFunction = () => {}) {
if (process.env.FPL_REGISTRY) {
if (!hasLoggedCustomRegistry) {
hasLoggedCustomRegistry = true;
log(
'info',
`Using custom registry specified by FPL_REGISTRY environment variable: ${process.env.FPL_REGISTRY}`
);
}
return process.env.FPL_REGISTRY;
}
}

export async function getDistUrl(
registry: string,
packageName: string,
version: string
): Promise<string> {
const cleanedRegistry = registry.replace(/\/$/, '');
// 1 get the manifest information about the package from the registry
const res = await axiosGet(`${cleanedRegistry}/${packageName}`);
// 2 find the NPM tarball location
const npmLocation = res.data?.versions?.[version]?.dist?.tarball;

// 3 if found, use it, otherwise fallback to the FHIR spec location
if (npmLocation) {
return npmLocation;
} else {
return `${cleanedRegistry}/${packageName}/${version}`;
}
}
17 changes: 7 additions & 10 deletions src/PackageLoader.ts → src/loader/PackageLoader.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import fs from 'fs-extra';
import os from 'os';
import path from 'path';
import fs from 'fs-extra';
import { PackageDB } from './PackageDB';
import { LogFunction } from './utils';
import { RegistryClient } from './RegistryClient';
import { PackageStats } from './PackageStats';
import { CurrentBuildClient } from './CurrentBuildClient';
import { InvalidPackageError } from './errors/InvalidPackageError';
import { InvalidResourceError } from './errors/InvalidResourceError';
import { PackageInfo } from './PackageInfo';
import { ResourceInfo } from './ResourceInfo';
import { CurrentBuildClient } from '../current';
import { PackageDB } from '../db';
import { InvalidPackageError, InvalidResourceError } from '../errors';
import { PackageInfo, PackageStats, ResourceInfo } from '../package';
import { RegistryClient } from '../registry';
import { LogFunction } from '../utils';

export type PackageLoaderOptions = {
log?: LogFunction;
Expand Down
1 change: 1 addition & 0 deletions src/loader/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './PackageLoader';
File renamed without changes.
File renamed without changes.
File renamed without changes.
3 changes: 3 additions & 0 deletions src/package/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './PackageInfo';
export * from './PackageStats';
export * from './ResourceInfo';
42 changes: 42 additions & 0 deletions src/registry/DefaultRegistryClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { LogFunction } from '../utils';
import { FHIRRegistryClient } from './FHIRRegistryClient';
import { NPMRegistryClient } from './NPMRegistryClient';
import { RedundantRegistryClient } from './RedundantRegistryClient';
import { RegistryClient, RegistryClientOptions } from './RegistryClient';

const FHIR_PACKAGES_ENDPOINT = 'https://packages.fhir.org';
const FHIR_PACKAGES2_ENDPOINT = 'https://packages2.fhir.org/packages';

export class DefaultRegistryClient extends RedundantRegistryClient {
constructor(options?: RegistryClientOptions) {
let clients: RegistryClient[];
// If a custom registry has been specified, use that
const customRegistry = getCustomRegistry(options.log);
if (customRegistry) {
clients = [new NPMRegistryClient(customRegistry, options)];
}
// Otherwise use packages.fhir.org w/ packages2.fhir.org fallback
else {
clients = [
new FHIRRegistryClient(FHIR_PACKAGES_ENDPOINT, options),
new FHIRRegistryClient(FHIR_PACKAGES2_ENDPOINT, options)
];
}
super(clients, options);
}
}

let hasLoggedCustomRegistry = false;

function getCustomRegistry(log: LogFunction = () => {}) {
if (process.env.FPL_REGISTRY) {
if (!hasLoggedCustomRegistry) {
hasLoggedCustomRegistry = true;
log(
'info',
`Using custom registry specified by FPL_REGISTRY environment variable: ${process.env.FPL_REGISTRY}`
);
}
return process.env.FPL_REGISTRY;
}
}
21 changes: 21 additions & 0 deletions src/registry/FHIRRegistryClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { downloadPackageTarballToCache } from '../download';
import { LogFunction } from '../utils';
import { RegistryClient, RegistryClientOptions } from './RegistryClient';

export class FHIRRegistryClient implements RegistryClient {
public endpoint: string;
private log: LogFunction;

constructor(endpoint: string, options?: RegistryClientOptions) {
// Remove trailing '/' from endpoint if applicable
this.endpoint = endpoint.replace(/\/$/, '');
this.log = options.log ?? (() => {});
}

async download(name: string, version: string, cachePath: string): Promise<string> {
// Construct URL from endpoint, name, and version
// See: https://confluence.hl7.org/pages/viewpage.action?pageId=97454344#FHIRPackageRegistryUserDocumentation-Download
const url = `${this.endpoint}/${name}/${version}`;
return downloadPackageTarballToCache(name, version, url, cachePath, this.log);
}
}
27 changes: 27 additions & 0 deletions src/registry/NPMRegistryClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { downloadPackageTarballToCache } from '../download';
import { LogFunction, axiosGet } from '../utils';
import { RegistryClient, RegistryClientOptions } from './RegistryClient';

export class NPMRegistryClient implements RegistryClient {
public endpoint: string;
private log: LogFunction;

constructor(endpoint: string, options?: RegistryClientOptions) {
// Remove trailing '/' from endpoint if applicable
this.endpoint = endpoint.replace(/\/$/, '');
this.log = options.log ?? (() => {});
}

async download(name: string, version: string, cachePath: string): Promise<string> {
// Get the manifest information about the package from the registry
const res = await axiosGet(`${this.endpoint}/${name}`);
// Find the NPM tarball location in the manifest
let url = res.data?.versions?.[version]?.dist?.tarball;
// If tarball URL is not found, fallback to standard NPM approach per
// https://docs.fire.ly/projects/Simplifier/features/api.html#package-server-api
if (!url) {
url = `${this.endpoint}/${name}/-/${name}-${version}.tgz`;
}
return downloadPackageTarballToCache(name, version, url, cachePath, this.log);
}
}
Loading

0 comments on commit 25fba29

Please sign in to comment.