-
Olivier Giorgis authoredOlivier Giorgis authored
cubicweb.ts 15.60 KiB
import { useClient } from "@/context/ClientContext";
import { CWFile, DataService, ImportProcess, Project, Recipe } from "@/types";
import {
RQLParams,
BinariesParam,
ResultSet,
Transaction,
TransactionQueryScalarRef,
} from "@cubicweb/client";
import { useRouter } from "next/navigation";
export function useApiLogin() {
const client = useClient();
return (login: string, password: string) => client.login(login, password);
}
export function useApiLogout() {
const client = useClient();
const router = useRouter();
return async () => {
await client.logout();
router.push("/login");
};
}
export function useApiDeleteProject() {
const client = useClient();
const rql = "DELETE ImportProcedure X WHERE X eid %(eid)s";
return (eid: number) => client.execute(rql, { eid });
}
export function useApiDeleteDataService() {
const client = useClient();
const rql = "DELETE DataService X WHERE X eid %(eid)s";
return (eid: number) => client.execute(rql, { eid });
}
export function useApiDeleteRecipe() {
const client = useClient();
const rql = "DELETE ImportRecipe X WHERE X eid %(eid)s";
return (eid: number) => client.execute(rql, { eid });
}
function pushSetProjectFiles(
transaction: Transaction,
eid: number | TransactionQueryScalarRef,
data: Omit<Project, "import_recipes">,
) {
const insertFileRql =
"INSERT File X: X data %(data)s, X data_format %(data_format)s, X data_name %(data_name)s";
const setOntologyRql =
"Set X ontology_file %(ontology_file)s WHERE X eid %(eid)s";
const setShaclRql = "Set X shacl_files %(shacl_files)s WHERE X eid %(eid)s";
const binaries: BinariesParam = {};
if (data.ontology_file?.data) {
const file = data.ontology_file.data;
binaries["ontology_file"] = file;
const insertQuery = transaction.push(insertFileRql, {
data: {
type: "binary_reference",
ref: "ontology_file",
},
data_format: file.type,
data_name: "ontologie.owl",
});
transaction.push(setOntologyRql, {
eid,
ontology_file: insertQuery.ref().row(0).column(0),
});
}
if (data.shacl_files?.data) {
const file = data.shacl_files.data;
binaries["shacl_files"] = file;
const insertQuery = transaction.push(insertFileRql, {
data: {
type: "binary_reference",
ref: "shacl_files",
},
data_format: file.type,
data_name: "shacl.shacl",
});
transaction.push(setShaclRql, {
eid,
shacl_files: insertQuery.ref().row(0).column(0),
});
}
transaction.setBinaries(binaries);
}
export function useApiCreateProject() {
const client = useClient();
const rql =
"INSERT ImportProcedure X: X name %(name)s, X virtuoso_url %(virtuoso_url)s, X virtuoso_user %(virtuoso_user)s, " +
"X virtuoso_password %(virtuoso_password)s, X activated %(activated)s";
return async (data: Omit<Project, "import_recipes">) => {
const transaction = new Transaction();
const query = transaction.push(rql, {
name: data.name,
virtuoso_url: data.virtuoso_url,
virtuoso_user: data.virtuoso_user,
virtuoso_password: data.virtuoso_password,
activated: data.activated,
});
const eidRef = query.ref().row(0).column(0);
pushSetProjectFiles(transaction, eidRef, data);
const result = await client.executeTransaction(transaction);
return result.resolveScalar(eidRef) as number;
};
}
export function useApiUpdateProject() {
const client = useClient();
const rql =
"SET X name %(name)s, X virtuoso_url %(virtuoso_url)s, X virtuoso_user %(virtuoso_user)s, X virtuoso_password %(virtuoso_password)s, X activated %(activated)s" +
"WHERE X is ImportProcedure, X eid %(eid)s";
return (data: Omit<Project, "import_recipes">) => {
const transaction = new Transaction();
transaction.push(rql, {
eid: data.eid,
name: data.name,
virtuoso_url: data.virtuoso_url,
virtuoso_user: data.virtuoso_user,
virtuoso_password: data.virtuoso_password,
activated: data.activated,
});
pushSetProjectFiles(transaction, data.eid, data);
return client.executeTransaction(transaction);
};
}
export function useApiUpdateProjectState() {
const client = useClient();
const rql =
"SET X activated %(activated)s" +
"WHERE X is ImportProcedure, X eid %(eid)s";
return (eid: number, activated: boolean) =>
client.execute(rql, { eid, activated });
}
export function useApiCreateDataService() {
const client = useClient();
const rql =
"INSERT DataService X: X name %(name)s, X data_url %(data_url)s, X refresh_period %(refresh_period)s, X description %(description)s";
return (data: Omit<DataService, "eid" | "projects">) =>
client.execute(rql, {
name: data.name,
data_url: data.data_url,
refresh_period: data.refresh_period,
description: data.description ?? "",
});
}
export function useApiUpdateDataService() {
const client = useClient();
const rql =
"SET X name %(name)s, X data_url %(data_url)s, X refresh_period %(refresh_period)s, X description %(description)s" +
"WHERE X is DataService, X eid %(eid)s";
return (data: DataService) =>
client.execute(rql, {
eid: data.eid,
name: data.name,
data_url: data.data_url,
refresh_period: data.refresh_period,
description: data.description ?? "",
});
}
export class UnknownEidError extends Error {
constructor(eid: number) {
super();
this.message = eid.toString();
}
}
export function useApiCreateRecipe() {
const client = useClient();
const insertRql =
"INSERT ImportRecipe X: X name %(name)s, X graph_uri %(graph_uri)s, X process_type %(process_type)s";
const setDataServiceRql =
"SET X dataservice %(dataservice)s WHERE X eid %(eid)s";
const setProjectRql =
"SET X import_recipes %(import_recipes)s WHERE X eid %(project_eid)s";
return (projectEid: number, data: Omit<Recipe, "eid">) => {
const transaction = new Transaction();
const insertQuery = transaction.push(insertRql, {
name: data.name,
graph_uri: data.graph_uri,
process_type: data.process_type,
});
const eidRef = insertQuery.ref().row(0).column(0);
transaction.push(setDataServiceRql, {
dataservice: data.dataservice.eid,
eid: eidRef,
});
transaction.push(setProjectRql, {
import_recipes: eidRef,
project_eid: projectEid,
});
return client.executeTransaction(transaction);
};
}
export function useApiGetProject(): (eid: number) => Promise<Project> {
const client = useClient();
const projectRql =
"Any X, ATTR_NAME, ATTR_VIRTUOSO_URL, ATTR_VIRTUOSO_USER, ATTR_VIRTUOSO_PASSWORD, ATTR_ACTIVATED, REL_ONTOLOGY.download_url(), REL_SHACL.download_url() " +
"WHERE X is ImportProcedure, X eid %(eid)s, X name ATTR_NAME, X virtuoso_url ATTR_VIRTUOSO_URL, X virtuoso_user ATTR_VIRTUOSO_USER, " +
"X virtuoso_password ATTR_VIRTUOSO_PASSWORD, X activated ATTR_ACTIVATED, X ontology_file REL_ONTOLOGY?, X shacl_files REL_SHACL?";
const recipeListRql =
"Any X, ATTR_NAME, ATTR_GRAPH_URI, ATTR_PROCESS_TYPE ,REL_DATASERVICE, REL_DATASERVICE_NAME " +
"WHERE X is ImportRecipe, X name ATTR_NAME, X graph_uri ATTR_GRAPH_URI, X process_type ATTR_PROCESS_TYPE, X dataservice REL_DATASERVICE, REL_DATASERVICE name REL_DATASERVICE_NAME, O import_recipes X, O eid %(eid)s";
return async (eid: number) => {
try {
const transaction = new Transaction();
const projectQuery = transaction.push(projectRql, { eid });
const recipeListQuery = transaction.push(recipeListRql, { eid });
const result = await client.executeTransaction(transaction);
const projectResult = result.resolveQuery(projectQuery.ref());
const recipeListResult = result.resolveQuery(recipeListQuery.ref());
if (projectResult.length === 0) {
throw new UnknownEidError(eid);
}
const recipeListJsonResult: Array<Recipe> = recipeListResult.map(
(r) =>
({
eid: r[0],
name: r[1],
process_type: r[2],
graph_uri: r[3],
dataservice: { eid: r[4], name: r[5] },
}) as Recipe,
);
const r = projectResult[0];
return {
eid: r[0],
name: r[1],
virtuoso_url: r[2],
virtuoso_user: r[3],
virtuoso_password: r[4],
activated: r[5],
import_recipes: recipeListJsonResult,
ontology_file: { downloadUrl: r[6] } as CWFile,
shacl_files: { downloadUrl: r[7] } as CWFile,
} as Project;
} catch (e) {
if (e && typeof e === "object" && "title" in e) {
const errorData = e as { title: string };
if (errorData.title === "TypeResolverException") {
throw new UnknownEidError(eid);
}
}
throw e;
}
};
}
export function useApiGetImportProcesses(): (data: {
dataServiceEid?: number;
recipeEid?: number;
projectEid?: number;
}) => Promise<ImportProcess[]> {
const client = useClient();
function getImportRecipePart(hasDataService: boolean, hasRecipe: boolean) {
if (hasRecipe)
return "X import_recipe R, R name VAL_RECIPE, R eid %(recipe_eid)s, ";
if (hasDataService)
return "X import_recipe R, R name VAL_RECIPE, R dataservice D, D eid %(dataservice_eid)s, ";
return "X import_recipe R, R name VAL_RECIPE, ";
}
function getImportProcedurePart(hasProject: boolean) {
if (hasProject)
return "X import_procedure P, P name VAL_PROJECT, P eid %(project_eid)s, ";
return "X import_procedure P, P name VAL_PROJECT, ";
}
function getRql(
hasDataService: boolean,
hasRecipe: boolean,
hasProject: boolean,
) {
// if your entity type is "workflowable" then you can use the relation in_state to find out
// in which state the entity is (whatever the workflow you use). then you can use the wf_info_for on
// the state to fetch the TrInfo which leads to this state, and with the TrInfo entity you do have the
// creation_date, the comment and so on
return (
"Any X, MAX(VAL_DATE), VAL_RECIPE, VAL_PROJECT, VAL_STATE " +
"GROUPBY X, VAL_RECIPE, VAL_PROJECT, VAL_STATE " +
"ORDERBY MAX(VAL_DATE) DESC " +
"WHERE X is ImportProcess, " +
getImportRecipePart(hasDataService, hasRecipe) +
getImportProcedurePart(hasProject) +
"X in_state S, S name VAL_STATE, " +
"TR wf_info_for X, TR creation_date VAL_DATE"
);
}
return async ({ dataServiceEid, recipeEid, projectEid }) => {
try {
const transaction = new Transaction();
const params: RQLParams = {};
if (dataServiceEid) {
params["dataservice_eid"] = dataServiceEid;
}
if (recipeEid) {
params["recipe_eid"] = recipeEid;
}
if (projectEid) {
params["project_eid"] = projectEid;
}
const importProcessListQuery = transaction.push(
getRql(
dataServiceEid !== undefined,
recipeEid !== undefined,
projectEid !== undefined,
),
params,
);
// TODO fetch files for all import processes
// See https://forge.extranet.logilab.fr/cubicweb/RQL/-/merge_requests/99
const dataLogQuery = transaction.push(
"Any X, DATA.download_url(), LOG.download_url() WHERE X eid %(eid)s, " +
"X has_output_dataset DATA?, X import_report LOG?",
{ eid: importProcessListQuery.ref().row(0).column(0) },
);
const result = await client.executeTransaction(transaction);
const importProcessListResult = result.resolveQuery(
importProcessListQuery.ref(),
);
const dataLogListResult = result.resolveQuery(dataLogQuery.ref());
for (const result of importProcessListResult) {
const dataLog = dataLogListResult.find((e) => e[0] === result[0]);
if (dataLog != undefined) {
result[5] = dataLog[1];
result[6] = dataLog[2];
}
}
return importProcessListResultSetToObject(importProcessListResult);
} catch (e) {
if (e && typeof e === "object" && "title" in e) {
const errorData = e as { title: string };
throw new Error(errorData.title);
}
throw e;
}
};
}
function importProcessListResultSetToObject(
resultSet: ResultSet,
): ImportProcess[] {
return resultSet.map((r) => {
return {
eid: r[0],
date: r[1],
recipe: r[2],
project: r[3],
state: r[4],
dataset_url: r[5],
log_url: r[6],
} as ImportProcess;
});
}
function projectListResultSetToObject(resultSet: ResultSet) {
return resultSet.map(
(r) =>
({
eid: r[0],
name: r[1],
virtuoso_url: r[2],
virtuoso_user: r[3],
virtuoso_password: r[4],
activated: r[5],
}) as Project,
);
}
export function useApiGetDataService(): (eid: number) => Promise<DataService> {
const client = useClient();
const dataServiceRql =
"Any X, ATTR_NAME, ATTR_DATA_URL, ATTR_REFRESH_PERIOD, ATTR_DESCRIPTION " +
"WHERE X is DataService, X eid %(eid)s, X name ATTR_NAME, X data_url ATTR_DATA_URL, X refresh_period ATTR_REFRESH_PERIOD, X description ATTR_DESCRIPTION";
const projectListRql =
"Any X, ATTR_NAME, ATTR_VIRTUOSO_URL, ATTR_VIRTUOSO_USER, ATTR_VIRTUOSO_PASSWORD, ATTR_ACTIVATED " +
"WHERE X is ImportProcedure, X name ATTR_NAME, X virtuoso_url ATTR_VIRTUOSO_URL, X virtuoso_user ATTR_VIRTUOSO_USER, X virtuoso_password ATTR_VIRTUOSO_PASSWORD, X activated ATTR_ACTIVATED, " +
"X import_recipes R, R dataservice %(eid)s";
return async (eid: number) => {
try {
const transaction = new Transaction();
const dataServiceQuery = transaction.push(dataServiceRql, { eid });
const projectListQuery = transaction.push(projectListRql, { eid });
const result = await client.executeTransaction(transaction);
const dataServiceResult = result.resolveQuery(dataServiceQuery.ref());
const projectListResult = result.resolveQuery(projectListQuery.ref());
if (dataServiceResult.length === 0) {
throw new UnknownEidError(eid);
}
const r = dataServiceResult[0];
return {
eid: r[0],
name: r[1],
data_url: r[2],
refresh_period: r[3],
description: r[4],
projects: projectListResultSetToObject(projectListResult),
} as DataService;
} catch (e) {
if (e && typeof e === "object" && "title" in e) {
const errorData = e as { title: string };
if (errorData.title === "TypeResolverException") {
throw new UnknownEidError(eid);
}
}
throw e;
}
};
}
export function useApiGetProjectList(): () => Promise<Array<Project>> {
const client = useClient();
const rql =
"Any X, ATTR_NAME, ATTR_VIRTUOSO_URL, ATTR_VIRTUOSO_USER, ATTR_VIRTUOSO_PASSWORD, ATTR_ACTIVATED " +
"WHERE X is ImportProcedure, X name ATTR_NAME, X virtuoso_url ATTR_VIRTUOSO_URL, X virtuoso_user ATTR_VIRTUOSO_USER, X virtuoso_password ATTR_VIRTUOSO_PASSWORD, X activated ATTR_ACTIVATED";
return async () => {
const result = await client.execute(rql, {});
return projectListResultSetToObject(result);
};
}
export function useApiGetDataServiceList(): () => Promise<Array<DataService>> {
const client = useClient();
const rql =
"Any X, ATTR_NAME, ATTR_DATA_URL, ATTR_REFRESH_PERIOD, ATTR_DESCRIPTION " +
"WHERE X is DataService, X name ATTR_NAME, X data_url ATTR_DATA_URL, X refresh_period ATTR_REFRESH_PERIOD, X description ATTR_DESCRIPTION";
return async () => {
const result = await client.execute(rql, {});
const jsonResult: Array<DataService> = result.map(
(r) =>
({
eid: r[0],
name: r[1],
data_url: r[2],
refresh_period: r[3],
description: r[4],
}) as DataService,
);
return jsonResult;
};
}