benchling_typescript_sdk
Version:
Typescript SDK for Benchling API
214 lines (204 loc) • 7.18 kB
text/typescript
import { BenchlingClient } from "../BenchlingClient";
import { AppConfigItem, AppConfigType, Schema } from "../types";
export type KeyName = string;
export type SchemaId = string;
export type SubKeyName = string;
export type FieldId = string;
export type FieldDisplayName = string;
export type ConfigItem = {
id: string | null;
name: string | null;
value: any;
type: AppConfigType;
description: string | null;
f: Record<SubKeyName, ConfigFItem>;
};
export type ConfigFItem = {
id: string | null;
system_name: string | null;
value: any;
type: AppConfigType;
description: string | null;
displayName: string | null;
};
export type SchemaTypes = Partial<Record<AppConfigType, SchemaId[]>>;
export type fieldIdDisplayName = Record<FieldId, FieldDisplayName>;
export class AppConfigItems {
private benchling: BenchlingClient;
constructor(client: BenchlingClient) {
this.benchling = client;
}
public async getAppConfigurations(appId: string): Promise<Record<KeyName, ConfigItem>> {
let appConfigurationPageGenerator = this.benchling.apps.listAppConfigurationItemsNoLimits({
appId,
});
let configs: AppConfigItem[] = [];
// print type of appConfigurationPageGenerator
for await (const page of appConfigurationPageGenerator) {
for (const item of page) {
configs.push(item);
}
}
let object = this.compileAppConfigItems(configs);
return object;
}
private async collectSchemas(
schemaTypes: Partial<Record<AppConfigType, string[]>>
): Promise<Schema[]> {
let schemas: Schema[] = [];
for (const [t, schemaIds] of Object.entries(schemaTypes)) {
let type = t as AppConfigType;
if (schemaIds.length > 0) {
if (type === "entity_schema") {
for (const schemaId of schemaIds) {
let schema = await this.benchling.schemas.getEntitySchema(schemaId);
schemas.push(schema);
}
} else if (type === "container_schema") {
for (const schemaId of schemaIds) {
let schema = await this.benchling.schemas.getContainerSchema(schemaId);
schemas.push(schema);
}
} else if (type === "location_schema") {
for (const schemaId of schemaIds) {
let schema = await this.benchling.schemas.getLocationSchema(schemaId);
schemas.push(schema);
}
} else if (type === "box_schema") {
for (const schemaId of schemaIds) {
let schema = await this.benchling.schemas.getBoxSchema(schemaId);
schemas.push(schema);
}
} else if (type === "plate_schema") {
for (const schemaId of schemaIds) {
let schema = await this.benchling.schemas.getPlateSchema(schemaId);
schemas.push(schema);
}
} else if (type === "result_schema") {
for (const schemaId of schemaIds) {
let schema = await this.benchling.schemas.getAssayResultSchema(schemaId);
schemas.push(schema);
}
} else if (type === "run_schema") {
for (const schemaId of schemaIds) {
let schema = await this.benchling.schemas.getAssayRunSchema(schemaId);
schemas.push(schema);
}
} else {
console.warn(`Unknown schema type: ${type}`);
}
}
}
return schemas;
}
private async compileAppConfigItems(
appConfigItems: AppConfigItem[]
): Promise<Record<string, ConfigItem>> {
let schemaTypes: SchemaTypes = {};
let schemaKeyNameFields: Record<KeyName, FieldId[]> = {};
let schemaKeyNameIds: Record<KeyName, SchemaId> = {};
let compiledConfigs = AppConfigItems.preCompileConfigItems(
appConfigItems,
schemaTypes,
schemaKeyNameFields,
schemaKeyNameIds
);
let fieldIdDisplayNames: Record<FieldId, string> = {};
// Benchling returns the SYSTEM_NAMES of fields but the API requires DISPLAY_NAMES of fields
// so I need to look up the schemas themselves for fucks sake
let schemas: Schema[] = await this.collectSchemas(schemaTypes);
for (const schema of schemas) {
if (!schema.fieldDefinitions) {
continue;
}
for (const fieldDef of schema.fieldDefinitions) {
if (fieldDef.id && fieldDef.name) {
fieldIdDisplayNames[fieldDef.id] = fieldDef.displayName || fieldDef.name;
}
}
}
for (const [key, configItem] of Object.entries(compiledConfigs)) {
if (configItem.f) {
for (const [subKey, field] of Object.entries(configItem.f)) {
field.displayName = field.id ? fieldIdDisplayNames[field.id] : null;
}
}
}
return compiledConfigs;
}
static preCompileConfigItems(
appConfigItems: AppConfigItem[],
schemaTypes: SchemaTypes, // mutates
schemaKeyNameFields: Record<KeyName, FieldId[]>, // mutates
schemaKeyNameIds: Record<KeyName, SchemaId> // mutates
): Record<KeyName, ConfigItem> {
// sort appConfigItems by path length
let res: Record<KeyName, ConfigItem> = {};
// load single paths first
let singlePaths = appConfigItems.filter((item) => item.path && item.path.length === 1);
// load nested paths second.
let nestedPaths = appConfigItems.filter((item) => item.path && item.path.length === 2);
// initiate schema field definitions
for (const item of singlePaths) {
let { id, value, type, name, description, path } = AppConfigItems.getItemAttributes(item);
res[path[0]] = { id, name, value, type, description, f: {} };
if (!id) {
continue;
}
schemaKeyNameIds[path[0]] = id;
if (!schemaTypes[type]) {
schemaTypes[type] = [id];
} else {
schemaTypes[type].push(id);
}
}
for (const item of nestedPaths) {
let { id, value, type, name, description, path } = AppConfigItems.getItemAttributes(item);
res[path[0]].f[path[1]] = {
id,
system_name: name,
value,
description,
type,
displayName: null, // must look up schema to get the display names
};
if (!id) {
continue;
}
if (!schemaKeyNameFields[path[0]]) {
schemaKeyNameFields[path[0]] = [id];
} else {
schemaKeyNameFields[path[0]].push(id);
}
}
return res;
}
static getItemAttributes(item: AppConfigItem): {
id: string | null;
name: string | null;
value: any;
description: string | null;
type: AppConfigType;
path: string[];
} {
let id: string | null = null;
let name: string | null = null;
let value: any;
let description: string | null = null;
let type = item.type as AppConfigType;
let path = item.path || [];
if ("description" in item) {
description = item.description || null;
}
if ("linkedResource" in item) {
if (item.linkedResource && "id" in item.linkedResource) {
id = item.linkedResource.id || null;
name = item.linkedResource.name || null;
}
}
if ("value" in item) {
value = item.value || null;
}
return { id, name, value, description, type, path };
}
}