UNPKG

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
'use strict'; 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