Skip to content
Snippets Groups Projects
cubicweb.ts 15.6 KiB
Newer Older
import { useClient } from "@/context/ClientContext";
import { CWFile, DataService, ImportProcess, Project, Recipe } from "@/types";
import {
  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();
    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();
    "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, {
      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();
    "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();
    "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?";
    "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],
            dataservice: { eid: r[4], name: r[5] },
          }) as Recipe,
      );

      const r = projectResult[0];
      return {
        eid: r[0],
        name: r[1],
        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_user: r[3],
        virtuoso_password: r[4],
        activated: r[5],
export function useApiGetDataService(): (eid: number) => Promise<DataService> {
  const client = useClient();
    "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";

    "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);
      }
        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;
  };
}