Skip to content
Snippets Groups Projects
Commit 9ca18cea9d79 authored by Frank Bessou's avatar Frank Bessou :spider_web:
Browse files

refactor: extract createDataProvider in its own file

parent 0aa46f86d5cf
No related branches found
No related tags found
1 merge request!11Topic/default/chores
import { client } from "@logilab/cwclientlibjs";
import { AuthProvider, DataProvider } from "ra-core";
import { Schema } from "./Schema";
import { deduplicate } from "./utils/deduplicate";
// import authProvider from './authProvider';
// see https://marmelab.com/react-admin/Authentication.html
export const authProvider: AuthProvider = {
// authentication
login: (_params) => Promise.resolve(),
checkError: (_error) => Promise.resolve(),
checkAuth: (_params) => Promise.resolve(),
logout: () => Promise.resolve(),
getIdentity: () => Promise.resolve({ id: 0 }),
// authorization
getPermissions: (_params) => Promise.resolve(),
};
export function createDataProvider<S extends Schema<string>>(
rqlClient: client.CwRqlClient,
schema: S
): DataProvider {
function subjectRelationsNames(entityType: string) {
return deduplicate(
schema.relationsDefinitions
.filter(({ subject }) => subject === entityType)
.map(({ name }) => name)
);
}
function objectRelationsNames(entityType: string) {
return deduplicate(
schema.relationsDefinitions
.filter(({ object }) => object === entityType)
.map(({ name }) => name)
);
}
const getList: DataProvider["getList"] = async (
resource: string,
{ pagination, sort, filter }
) => {
const sortAttribute = sort.field === "id" ? "eid" : sort.field;
const attributesNames = ["eid", ...Object.keys(schema.entities[resource])];
const selection: string[] = [];
const restrictions: string[] = [];
let sortvariable = null;
attributesNames.forEach((key, idx) => {
const variable = `X${idx}`;
selection.push(variable);
restrictions.push(`X ${key} ${variable}`);
if (key === sortAttribute) {
sortvariable = variable;
}
});
// Handle filters
restrictions.push(
...Object.entries(filter).map(([attrName, attrValue]) => {
return `EXISTS(X ${attrName} ~= '%${attrValue}%')`;
})
);
const total = await rqlClient
.queryRows(`Any Count(${selection[0]}) WHERE ${restrictions.join(",")}`)
.then((rows) => rows[0][0]);
return rqlClient
.queryRows(
`Any ${selection.join(", ")} ORDERBY ${sortvariable} ${
sort.order
} LIMIT ${pagination.perPage} OFFSET ${
(pagination.page - 1) * pagination.perPage
} WHERE ${restrictions.join(", ")}`,
{}
)
.then((rows) => {
return {
data: rows.map((row) =>
row.reduce(
(agg, attributeValue, idx) => ({
[attributesNames[idx]]: attributeValue,
...agg,
}),
{ id: row[0] }
)
),
total,
};
});
};
const getOne: DataProvider["getOne"] = async (resource: string, params) => {
// Getting attributes
const attributesNames = [...Object.keys(schema.entities[resource])];
const selection: string[] = [];
const restrictions: string[] = [];
attributesNames.forEach((key, idx) => {
const variable = `X${idx}`;
selection.push(variable);
restrictions.push(`X ${key} ${variable}`);
});
const result = await rqlClient.queryRows(
`Any ${selection.join(",")} Where ${restrictions.join(",")}, X eid ${
params.id
}`
);
const row = result[0];
const entity = row.reduce(
(agg, attributeValue, idx) => ({
[attributesNames[idx]]: attributeValue,
...agg,
}),
{ eid: params.id, id: params.id }
);
// Getting subject relations
const subjectRelations = schema.relationsDefinitions.filter(
({ subject }) => subject === resource
);
for (const relation of subjectRelations) {
const result = await rqlClient.queryRows(
`Any TARGET Where X ${relation.name} TARGET, X eid ${params.id}, TARGET is ${relation.object}`
);
(entity[relation.name] ??= []).push(...result.map((row) => row[0]));
}
// Getting object relations
const objectRelations = schema.relationsDefinitions.filter(
({ object }) => object === resource
);
for (const relation of objectRelations) {
const result = await rqlClient.queryRows(
`Any TARGET Where TARGET ${relation.name} X, X eid ${params.id}, TARGET is ${relation.subject}`
);
(entity[`reverse_${relation.name}`] ??= []).push(
...result.map((row) => row[0])
);
}
return { data: entity };
};
const getMany: DataProvider["getMany"] = async (resource, params) => {
// FIXME handle several params id and refactor code to use GetOne
// Getting attributes
const attributesNames = [...Object.keys(schema.entities[resource])];
const selection: string[] = [];
const restrictions: string[] = [];
attributesNames.forEach((key, idx) => {
const variable = `X${idx}`;
selection.push(variable);
restrictions.push(`X ${key} ${variable}`);
});
const result = await rqlClient.queryRows(
`Any ${selection.join(",")} Where ${restrictions.join(
","
)}, X eid IN (${params.ids.join(",")})`
);
const entities = result.map((row, rowIdx) =>
row.reduce(
(agg, attributeValue, idx) => ({
[attributesNames[idx]]: attributeValue,
...agg,
}),
{ id: params.ids[rowIdx] }
)
);
return { data: entities };
};
const getManyReference: DataProvider["getManyReference"] = (
_resource,
_params
) => Promise.reject("Not implemented");
const update: DataProvider["update"] = async (resource, { data, id }) => {
// FIXME update relations
const attributesUpdates: string[] = [];
Object.entries(data).forEach(([key, value]) => {
if (key in schema.entities[resource]) {
attributesUpdates.push(`X ${key} ${JSON.stringify(value)}`);
}
});
await rqlClient.queryRows(`
SET ${attributesUpdates.join(", ")} WHERE X is ${resource}, X eid ${id}
`);
return Promise.resolve({ data: { ...data, id } });
};
const updateMany: DataProvider["updateMany"] = (_resource, _params) =>
Promise.reject("Not implemented");
const create: DataProvider["create"] = async (resource, { data }) => {
// FIXME create relations
const assignments: string[] = [];
const restrictions: string[] = [];
Object.entries(data).forEach(([key, value]) => {
if (key in schema.entities[resource]) {
assignments.push(`X ${key} ${JSON.stringify(value)}`);
}
});
let counter = 0;
function addRelation(
relationName: string,
resourceRole: "subject" | "object",
targetId: number
) {
const relationTargetVariable = `Y${counter++}`;
if (resourceRole === "subject") {
assignments.push(`X ${relationName} ${relationTargetVariable}`);
} else {
assignments.push(`${relationTargetVariable} ${relationName} X`);
}
restrictions.push(`${relationTargetVariable} eid ${targetId}`);
}
function toArray(idOrIds: number | number[] | undefined) {
if (Array.isArray(idOrIds)) {
return idOrIds;
}
if (idOrIds === undefined) {
return [];
}
return [idOrIds];
}
for (const relationName of subjectRelationsNames(resource)) {
const ids = toArray(data[relationName]);
ids.forEach((id) => addRelation(relationName, "subject", id));
}
for (const relationName of objectRelationsNames(resource)) {
const ids = toArray(data[`reverse_${relationName}`]);
ids.forEach((id) => addRelation(relationName, "object", id));
}
const result = await rqlClient.queryRows(`
INSERT ${resource} X:
${assignments.join(", ")}
${restrictions.length > 0 ? "WHERE " + restrictions.join(", ") : ""}
`);
const eid = result[0][0];
return Promise.resolve({ data: { ...data, id: eid } });
};
const _delete: DataProvider["delete"] = (_resource, _params) =>
Promise.reject("Not implemented");
const deleteMany: DataProvider["deleteMany"] = (_resource, _params) =>
Promise.reject("Not implemented");
return {
getList,
getOne,
getMany,
getManyReference,
update,
updateMany,
create,
delete: _delete,
deleteMany,
};
}
export { createDataProvider } from "./createDataProvider";
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment