refine-apito
Version:
A data provider for Refine that connects to Apito - a headless CMS and backend builder.
792 lines (786 loc) • 31.8 kB
JavaScript
;
var core = require('@urql/core');
var pluralize = require('pluralize');
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
var pluralize__default = /*#__PURE__*/_interopDefault(pluralize);
// src/provider.ts
var handleGraphQLError = (error, onTokenExpired) => {
if (!error) {
return {
message: "Unknown error occurred",
statusCode: 500
};
}
if (error.networkError) {
const statusCode = error.networkError.statusCode || error.networkError.status;
if (statusCode === 403 || statusCode === 401) {
console.log("Token expired (403/401), triggering logout...");
onTokenExpired == null ? void 0 : onTokenExpired();
return {
message: "Token expired. Please login again.",
statusCode: 403
};
}
return {
message: `Network error: ${error.networkError.message}`,
statusCode: statusCode || 503
// Service Unavailable
};
}
if (error.graphQLErrors && error.graphQLErrors.length > 0) {
const hasAuthError = error.graphQLErrors.some(
(err) => err.message.toLowerCase().includes("unauthorized") || err.message.toLowerCase().includes("forbidden") || err.message.toLowerCase().includes("token") || err.message.toLowerCase().includes("authentication") || err.message.toLowerCase().includes("authorization")
);
if (hasAuthError) {
console.log(
"Authentication error detected in GraphQL, triggering logout..."
);
onTokenExpired == null ? void 0 : onTokenExpired();
return {
message: "Authentication failed. Please login again.",
statusCode: 403
};
}
const errorMessages = error.graphQLErrors.map((err) => err.message).join(", ");
return {
message: errorMessages,
statusCode: 400
// Bad Request for GraphQL validation errors
};
}
return {
message: error.message || "An error occurred during the GraphQL operation",
statusCode: 400
};
};
var generateConnectionFields = (connectionFields, aliasFields) => {
return Object.keys(connectionFields).map((key) => {
if (aliasFields[key]) {
return `${key}: ${aliasFields[key]} { ${connectionFields[key]} }`;
} else {
return `${key} { ${connectionFields[key]} }`;
}
}).join("\n");
};
var apitoDataProvider = (apiUrl, token, onTokenExpired) => {
const client = new core.Client({
url: apiUrl,
exchanges: [core.cacheExchange, core.fetchExchange],
fetchOptions: () => ({
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
}
}),
preferGetMethod: false
});
return {
getApiUrl: () => apiUrl,
getApiClient: () => {
return new core.Client({
url: apiUrl,
exchanges: [core.cacheExchange, core.fetchExchange],
fetchOptions: () => ({
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
}
}),
preferGetMethod: false
});
},
getToken: () => token,
async getList(params) {
var _a, _b, _c, _d;
try {
const { resource, filters, sorters, pagination, meta } = params;
const connectionFields = (meta == null ? void 0 : meta.connectionFields) || {};
const aliasFields = (meta == null ? void 0 : meta.aliasFields) || {};
const reverseLookup = (meta == null ? void 0 : meta.reverseLookup) || {};
let data = [];
let total = 0;
let query = null;
let variables = null;
if (meta == null ? void 0 : meta.gqlQuery) {
query = meta.gqlQuery;
variables = meta.variables;
const queryKey = meta.queryKey || resource;
const response = await client.query(query, variables).toPromise();
if (response.error) {
return Promise.reject(
handleGraphQLError(response.error, onTokenExpired)
);
}
const queryResponse = (_a = response == null ? void 0 : response.data) == null ? void 0 : _a[queryKey];
const responseData = Array.isArray(queryResponse) ? queryResponse : [];
const responseTotal = responseData.length ?? 0;
return {
data: responseData,
total: responseTotal
};
} else {
const fields = (meta == null ? void 0 : meta.fields) || ["id"];
const pluralResource = pluralize__default.default.plural(resource).charAt(0).toUpperCase() + pluralize__default.default.plural(resource).slice(1);
const processFilter = (filter) => {
const { field, operator, value } = filter;
if (operator === "eq" && Array.isArray(value)) {
const nestedCondition = {};
value.forEach((condition) => {
const {
field: subField,
operator: subOperator,
value: subValue
} = condition;
if (subField && subOperator && subValue !== void 0) {
if (!nestedCondition[subField]) {
nestedCondition[subField] = {};
}
nestedCondition[subField][subOperator] = subValue;
}
});
return { [field]: nestedCondition };
}
if (operator === "or" && Array.isArray(value)) {
const orConditions = {};
value.forEach((condition) => {
const { field: field2, operator: operator2, value: value2 } = condition;
if (field2 && operator2 && value2 !== void 0) {
const adjustedField = field2.startsWith("data.") ? field2.replace("data.", "") : field2;
orConditions[adjustedField] = { [operator2]: value2 };
}
});
return { OR: orConditions };
}
if (operator === "and" && Array.isArray(value)) {
const andConditions = {};
value.forEach((condition) => {
const { field: field2, operator: operator2, value: value2 } = condition;
if (field2 && operator2 && value2 !== void 0) {
const adjustedField = field2.startsWith("data.") ? field2.replace("data.", "") : field2;
andConditions[adjustedField] = { [operator2]: value2 };
}
});
return { AND: andConditions };
}
if (field === "_key") {
return { _key: { [operator || "eq"]: value } };
}
if (field && field.includes("relation.")) {
const relationPath = field.replace("relation.", "").split(".");
const relationCondition = {};
let current = relationCondition;
for (let i = 0; i < relationPath.length - 1; i++) {
const part = relationPath[i];
if (!current[part]) {
current[part] = {};
}
current = current[part];
}
const lastPart = relationPath[relationPath.length - 1];
if (operator && value !== void 0) {
current[lastPart] = { [operator]: value };
}
return { relation: relationCondition };
}
if (operator && value !== void 0) {
const adjustedField = field.startsWith("data.") ? field.replace("data.", "") : field;
return { [adjustedField]: { [operator]: value } };
}
return {};
};
let _key = null;
let relationWhere = null;
let where = {};
if (filters && filters.length > 0) {
filters.forEach((filter) => {
const processed = processFilter(filter);
if (processed._key) {
_key = processed._key;
} else if (processed.relation) {
if (!relationWhere) {
relationWhere = {};
}
Object.assign(relationWhere, processed.relation);
} else if (processed.OR) {
where.OR = processed.OR;
} else if (processed.AND) {
where.AND = processed.AND;
} else {
Object.assign(where, processed);
}
});
}
const hasKey = _key !== null;
const hasRelationWhere = relationWhere !== null;
const queryVariables = [
hasKey ? `$_key: ${resource.toUpperCase()}LIST_KEY_CONDITION` : null,
`$connection: ${resource.toUpperCase()}_CONNECTION_FILTER_CONDITION`,
`$where: ${resource.toUpperCase()}LIST_INPUT_WHERE_PAYLOAD`,
hasRelationWhere ? `$relationWhere: ${resource.toUpperCase()}_WHERE_RELATION_FILTER_CONDITION` : null,
hasKey ? `$_keyCount: ${resource.toUpperCase()}LIST_COUNT_KEY_CONDITION` : null,
`$whereCount: ${resource.toUpperCase()}LIST_COUNT_INPUT_WHERE_PAYLOAD`,
hasRelationWhere ? `$relationWhereCount: ${resource.toUpperCase()}_WHERE_RELATION_FILTER_CONDITION` : null,
`$sort: ${resource.toUpperCase()}LIST_INPUT_SORT_PAYLOAD`,
`$page: Int`,
`$limit: Int`,
`$local: LOCAL_TYPE_ENUM`
].filter(Boolean).join("\n");
const queryArguments = [
hasKey ? "_key: $_key" : null,
"connection: $connection",
"where: $where",
hasRelationWhere ? "relation: $relationWhere" : null,
"sort: $sort",
"page: $page",
"limit: $limit",
"local: $local"
].filter(Boolean).join(", ");
const countArguments = [
hasKey ? "_key: $_keyCount" : null,
"connection: $connection",
"where: $whereCount",
hasRelationWhere ? "relation: $relationWhereCount" : null,
"page: $page",
"limit: $limit"
].filter(Boolean).join(", ");
query = core.gql`
query Get${pluralResource}(
${queryVariables}
) {
${resource}List(${queryArguments}) {
id
data {
${fields.join("\n")}
}
${generateConnectionFields(connectionFields, aliasFields)}
meta {
created_at
status
updated_at
}
}
${resource}ListCount(${countArguments}) {
total
}
}
`;
variables = {
...hasKey && { _key },
connection: reverseLookup || {},
where: where || {},
...hasRelationWhere && { relationWhere },
whereCount: where || {},
...hasKey && { _keyCount: _key },
...hasRelationWhere && { relationWhereCount: relationWhere },
sort: sorters == null ? void 0 : sorters.reduce((acc, sorter) => {
const { field, order } = sorter;
if (field && order) {
acc[field] = order.toUpperCase();
}
return acc;
}, {}),
page: (pagination == null ? void 0 : pagination.currentPage) || 1,
limit: (pagination == null ? void 0 : pagination.pageSize) || 10
};
const response = await client.query(query, variables).toPromise();
if (response.error) {
return Promise.reject(
handleGraphQLError(response.error, onTokenExpired)
);
}
data = ((_b = response == null ? void 0 : response.data) == null ? void 0 : _b[`${resource}List`]) ?? [];
total = "total" in (((_c = response == null ? void 0 : response.data) == null ? void 0 : _c[`${resource}ListCount`]) || {}) ? ((_d = response == null ? void 0 : response.data) == null ? void 0 : _d[`${resource}ListCount`]).total : 0;
}
return {
data,
total
};
} catch (error) {
if (error.statusCode !== void 0) {
return Promise.reject(error);
}
const httpError = {
message: (error == null ? void 0 : error.message) || "Failed to fetch list data",
statusCode: 500
};
return Promise.reject(httpError);
}
},
async getOne(params) {
var _a;
try {
const { resource, id, meta } = params;
const fields = (meta == null ? void 0 : meta.fields) || ["id"];
const connectionFields = (meta == null ? void 0 : meta.connectionFields) || {};
const aliasFields = (meta == null ? void 0 : meta.aliasFields) || {};
const singularResource = pluralize__default.default.singular(resource);
const query = core.gql`
query Get${singularResource.charAt(0).toUpperCase() + singularResource.slice(1)}($id: String!) {
${singularResource}(_id: $id) {
id
data {
${fields.join("\n")}
}
${generateConnectionFields(connectionFields, aliasFields)}
meta {
created_at
status
updated_at
}
}
}
`;
const response = await client.query(query, { id }).toPromise();
if (response.error) {
return Promise.reject(
handleGraphQLError(response.error, onTokenExpired)
);
}
const data = ((_a = response == null ? void 0 : response.data) == null ? void 0 : _a[singularResource]) ?? {};
return {
data
};
} catch (error) {
if (error.statusCode !== void 0) {
return Promise.reject(error);
}
const httpError = {
message: (error == null ? void 0 : error.message) || `Failed to fetch ${params.resource} with id ${params.id}`,
statusCode: 500
};
return Promise.reject(httpError);
}
},
async create(params) {
var _a, _b, _c;
try {
const { resource, variables, meta } = params;
let query = null;
let _variables = null;
if (meta == null ? void 0 : meta.gqlMutation) {
query = meta.gqlMutation;
if (variables) {
_variables = variables;
} else {
_variables = meta.variables;
}
const response = await client.mutation(query, _variables).toPromise();
if (response.error) {
return Promise.reject(handleGraphQLError(response.error));
}
return {
data: ((_b = (_a = response == null ? void 0 : response.data) == null ? void 0 : _a[`create${resource.charAt(0).toUpperCase() + resource.slice(1)}`]) == null ? void 0 : _b.data) ?? {}
};
} else {
try {
const { resource: resource2, variables: variables2, meta: meta2 } = params;
const singularResource = pluralize__default.default.singular(resource2);
const fields = (meta2 == null ? void 0 : meta2.fields) || ["id"];
const name = singularResource.charAt(0).toUpperCase() + singularResource.slice(1);
const query2 = core.gql`
mutation Create${name}($payload: ${name}_Create_Payload!, $connect: ${name}_Relation_Connect_Payload) {
create${name}(payload: $payload, connect: $connect, status: published) {
id
data {
${fields.join("\n")}
}
meta {
created_at
status
updated_at
}
}
}
`;
const variableData = variables2;
const response = await client.mutation(query2, {
payload: variableData.data,
connect: variableData.connect
}).toPromise();
if (response.error) {
return Promise.reject(
handleGraphQLError(response.error, onTokenExpired)
);
}
const data = ((_c = response == null ? void 0 : response.data) == null ? void 0 : _c[`create${name}`]) ?? {};
return { data };
} catch (error) {
if (error.statusCode !== void 0) {
return Promise.reject(error);
}
const httpError = {
message: (error == null ? void 0 : error.message) || `Failed to create ${params.resource}`,
statusCode: 500
};
return Promise.reject(httpError);
}
}
} catch (error) {
if (error.statusCode !== void 0) {
return Promise.reject(error);
}
const httpError = {
message: (error == null ? void 0 : error.message) || `Failed to create ${params.resource}`,
statusCode: 500
};
return Promise.reject(httpError);
}
},
async createMany(params) {
var _a;
try {
const { resource, variables, meta } = params;
const singularResource = pluralize__default.default.singular(resource);
const fields = (meta == null ? void 0 : meta.fields) || ["id"];
const name = singularResource.charAt(0).toUpperCase() + singularResource.slice(1);
const mutation = core.gql`
mutation Upsert${name}List($payloads: [${name}List_Upsert_Payload!]!, $connect: ${name}_Relation_Connect_Payload) {
upsert${name}List(payloads: $payloads, connect: $connect, status: published) {
id
data {
${fields.join("\n")}
}
meta {
created_at
status
updated_at
}
}
}
`;
const variableData = Array.isArray(variables) ? variables.filter(
(item) => item !== null && item !== void 0 && (typeof item !== "object" || Object.keys(item).length > 0)
) : variables;
const response = await client.mutation(mutation, {
payloads: variableData
//connect: variableData.connect,
}).toPromise();
if (response.error) {
return Promise.reject(
handleGraphQLError(response.error, onTokenExpired)
);
}
const data = ((_a = response == null ? void 0 : response.data) == null ? void 0 : _a[`upsert${name}List`]) ?? [];
return { data };
} catch (error) {
if (error.statusCode !== void 0) {
return Promise.reject(error);
}
const httpError = {
message: (error == null ? void 0 : error.message) || `Failed to create multiple ${params.resource} records`,
statusCode: 500
};
return Promise.reject(httpError);
}
},
async update({ resource, id, variables, meta }) {
var _a, _b, _c, _d;
try {
let query = null;
let _variables = null;
if (meta == null ? void 0 : meta.gqlMutation) {
query = meta.gqlMutation;
if (variables) {
_variables = variables;
} else {
_variables = meta.variables;
}
const response = await client.mutation(query, _variables).toPromise();
if (response.error) {
return Promise.reject(handleGraphQLError(response.error));
}
return {
data: ((_b = (_a = response == null ? void 0 : response.data) == null ? void 0 : _a[`update${resource.charAt(0).toUpperCase() + resource.slice(1)}`]) == null ? void 0 : _b.data) ?? {}
};
} else {
const fields = (meta == null ? void 0 : meta.fields) || ["id"];
const deltaUpdate = (meta == null ? void 0 : meta.deltaUpdate) || false;
const singularResource = pluralize__default.default.singular(resource);
const name = singularResource.charAt(0).toUpperCase() + singularResource.slice(1);
query = core.gql`
mutation Update${name}(
$id: String!,
$deltaUpdate: Boolean,
$payload: ${name}_Update_Payload!,
$connect: ${name}_Relation_Connect_Payload,
$disconnect: ${name}_Relation_Disconnect_Payload
) {
update${name}(_id: $id, deltaUpdate: $deltaUpdate, payload: $payload, connect: $connect, disconnect: $disconnect, status: published) {
id
data {
${fields.join("\n")}
}
meta {
created_at
status
updated_at
}
}
}
`;
_variables = {
id,
deltaUpdate,
payload: variables.data,
connect: variables.connect,
disconnect: variables.disconnect
};
const response = await client.mutation(query, _variables).toPromise();
if (response.error) {
return Promise.reject(
handleGraphQLError(response.error, onTokenExpired)
);
}
return {
data: ((_d = (_c = response == null ? void 0 : response.data) == null ? void 0 : _c[`update${resource.charAt(0).toUpperCase() + resource.slice(1)}`]) == null ? void 0 : _d.data) ?? {}
};
}
} catch (error) {
if (error.statusCode !== void 0) {
return Promise.reject(error);
}
const httpError = {
message: (error == null ? void 0 : error.message) || `Failed to update ${resource} with id ${id}`,
statusCode: 500
};
return Promise.reject(httpError);
}
},
async deleteOne({ resource, id }) {
var _a, _b, _c;
try {
const singularResource = pluralize__default.default.singular(resource);
const name = singularResource.charAt(0).toUpperCase() + singularResource.slice(1);
const query = core.gql`
mutation Delete${name}($ids: [String]!) {
delete${name}(_ids: $ids) {
response
}
}
`;
const response = await client.mutation(query, { ids: [id] }).toPromise();
if (response.error) {
return Promise.reject(
handleGraphQLError(response.error, onTokenExpired)
);
}
if (((_a = response.data) == null ? void 0 : _a.errors) && Array.isArray(response.data.errors)) {
const errorMessages = response.data.errors.map((err) => err.message).join(", ");
const httpError = {
message: errorMessages,
statusCode: 400
};
return Promise.reject(httpError);
}
return {
data: ((_c = (_b = response == null ? void 0 : response.data) == null ? void 0 : _b[`delete${resource.charAt(0).toUpperCase() + resource.slice(1)}`]) == null ? void 0 : _c.data) ?? {}
};
} catch (error) {
if (error.statusCode !== void 0) {
return Promise.reject(error);
}
const httpError = {
message: (error == null ? void 0 : error.message) || `Failed to delete ${resource} with id ${id}`,
statusCode: 500
};
return Promise.reject(httpError);
}
},
async custom(params) {
var _a, _b, _c, _d;
try {
const query = (_a = params == null ? void 0 : params.meta) == null ? void 0 : _a.gqlQuery;
const mutation = (_b = params == null ? void 0 : params.meta) == null ? void 0 : _b.gqlMutation;
let variables = (_c = params == null ? void 0 : params.meta) == null ? void 0 : _c.gqlVariables;
if (query && mutation) {
const httpError = {
message: "Query and mutation cannot both be provided for custom operation",
statusCode: 400
};
return Promise.reject(httpError);
}
if (!query && !mutation) {
const httpError = {
message: "Query or mutation is required for custom operation",
statusCode: 400
};
return Promise.reject(httpError);
}
const { filters } = params;
const where = filters == null ? void 0 : filters.reduce(
(acc, filter) => {
const { field, operator, value } = filter;
if (operator && value !== void 0) {
const adjustedField = field.startsWith("data.") ? field.replace("data.", "") : field;
acc[adjustedField] = { [operator || "eq"]: value };
}
return acc;
},
{}
);
if (where) {
variables = {
...variables,
where: where || {}
};
}
if ((variables == null ? void 0 : variables.payloads) && typeof variables.payloads === "object" && !Array.isArray(variables.payloads)) {
variables = {
...variables,
payloads: Object.values(variables.payloads)
};
}
let response = null;
if (query) {
response = await client.query(query, variables).toPromise();
} else if (mutation) {
response = await client.mutation(mutation, variables).toPromise();
} else {
throw new Error("No query or mutation provided");
}
if (response.error) {
return Promise.reject(
handleGraphQLError(response.error, onTokenExpired)
);
}
if (((_d = response.data) == null ? void 0 : _d.errors) && Array.isArray(response.data.errors)) {
const errorMessages = response.data.errors.map((err) => err.message).join(", ");
const httpError = {
message: errorMessages,
statusCode: 400
};
return Promise.reject(httpError);
}
return {
data: response == null ? void 0 : response.data
};
} catch (error) {
if (error.statusCode !== void 0) {
return Promise.reject(error);
}
const httpError = {
message: (error == null ? void 0 : error.message) || "Failed to execute custom operation",
statusCode: 500
};
return Promise.reject(httpError);
}
}
};
};
var provider_default = apitoDataProvider;
// src/debugProvider.ts
var debugApitoDataProvider = (apiUrl, token) => {
const provider = provider_default(apiUrl, token);
return {
...provider,
// Utility methods
getApiUrl: () => {
console.log("[Apito Debug] getApiUrl called");
return provider.getApiUrl();
},
getApiClient: () => {
console.log("[Apito Debug] getApiClient called");
return provider.getApiClient();
},
getToken: () => {
console.log("[Apito Debug] getToken called");
return provider.getToken();
},
// Data provider methods
getList: async (params) => {
console.log("[Apito Debug] getList called with params:", JSON.stringify(params, null, 2));
try {
const result = await provider.getList(params);
console.log("[Apito Debug] getList result:", JSON.stringify({
total: result.total,
data: result.data.length > 0 ? `${result.data.length} items` : "empty array"
}, null, 2));
return result;
} catch (error) {
console.error("[Apito Debug] getList error:", error);
throw error;
}
},
getOne: async (params) => {
console.log("[Apito Debug] getOne called with params:", JSON.stringify(params, null, 2));
try {
const result = await provider.getOne(params);
console.log("[Apito Debug] getOne result:", JSON.stringify({
id: result.data.id
}, null, 2));
return result;
} catch (error) {
console.error("[Apito Debug] getOne error:", error);
throw error;
}
},
create: async (params) => {
console.log("[Apito Debug] create called with params:", JSON.stringify(params, null, 2));
try {
const result = await provider.create(params);
console.log("[Apito Debug] create result:", JSON.stringify({
id: result.data.id
}, null, 2));
return result;
} catch (error) {
console.error("[Apito Debug] create error:", error);
throw error;
}
},
createMany: async (params) => {
console.log("[Apito Debug] createMany called with params:", JSON.stringify(params, null, 2));
try {
const createManyFn = provider.createMany;
const result = await createManyFn(params);
console.log("[Apito Debug] createMany result:", JSON.stringify({
count: Array.isArray(result.data) ? result.data.length : 0
}, null, 2));
return result;
} catch (error) {
console.error("[Apito Debug] createMany error:", error);
throw error;
}
},
update: async (params) => {
console.log("[Apito Debug] update called with params:", JSON.stringify(params, null, 2));
try {
const result = await provider.update(params);
console.log("[Apito Debug] update result:", JSON.stringify(result, null, 2));
return result;
} catch (error) {
console.error("[Apito Debug] update error:", error);
throw error;
}
},
deleteOne: async (params) => {
console.log("[Apito Debug] deleteOne called with params:", JSON.stringify(params, null, 2));
try {
const result = await provider.deleteOne(params);
console.log("[Apito Debug] deleteOne result:", JSON.stringify(result, null, 2));
return result;
} catch (error) {
console.error("[Apito Debug] deleteOne error:", error);
throw error;
}
},
custom: async (params) => {
console.log("[Apito Debug] custom called with params:", JSON.stringify(params, null, 2));
try {
const customFn = provider.custom;
const result = await customFn(params);
console.log("[Apito Debug] custom result:", JSON.stringify(result, null, 2));
return result;
} catch (error) {
console.error("[Apito Debug] custom error:", error);
throw error;
}
}
};
};
var debugProvider_default = debugApitoDataProvider;
exports.apitoDataProvider = provider_default;
exports.debugApitoDataProvider = debugProvider_default;
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map