From d91d47f62488d7947f25250afa3bdb388da69295 Mon Sep 17 00:00:00 2001 From: Cobey Potter Date: Sat, 10 Aug 2024 00:55:24 -0400 Subject: [PATCH] fix: update error catching to catch all produced errors --- examples/hooks.html | 4 ++-- lib/useButter.js | 27 +++++++++++++++++++++------ tests/basic.html | 4 ++-- tests/hooks.html | 4 ++-- tests/lib/butter.min.js | 2 +- 5 files changed, 28 insertions(+), 13 deletions(-) diff --git a/examples/hooks.html b/examples/hooks.html index 5b38f5a..202b808 100644 --- a/examples/hooks.html +++ b/examples/hooks.html @@ -58,7 +58,7 @@ } ) .catch( - (error) => console.error("post: ", err.message, err.cause) + (err) => console.error("post: ", err.message, err.cause) ); @@ -67,7 +67,7 @@ (res) => console.log(res) ) .catch( - (error) => console.error("page: ", err.message, err.cause) + (err) => console.error("page: ", err.message, err.cause) ) }); diff --git a/lib/useButter.js b/lib/useButter.js index 95b7a94..84daba4 100644 --- a/lib/useButter.js +++ b/lib/useButter.js @@ -119,9 +119,7 @@ export default function useButter(type, butterConfig) { */ catch (errorPayload) { if (errorPayload.response) { - const { - detail: errorDetail - } = await errorPayload.response.json(); + const errors = await errorPayload.response.json(); const mappedParams = Object.fromEntries( Object.entries(errorPayload.params) @@ -134,7 +132,7 @@ export default function useButter(type, butterConfig) { url.searchParams.delete('auth_token'); const cause = { - data: errorDetail, + data: errors, headers: errorPayload.response.headers, status: errorPayload.response.status, statusText: errorPayload.response.statusText, @@ -145,13 +143,30 @@ export default function useButter(type, butterConfig) { }; useOnError( - errorDetail, + errors, cause ); + /** + * Formats error messages from an object of errors. + * + * @param {Object} unformattedErrors - An object containing error messages. + * @returns {string} A formatted error message string. + */ + function formatErrors (unformattedErrors) { + const errorKeys = Object.keys(unformattedErrors); + + if (errorKeys.length > 1) { + return `Errors caught in [${ errorKeys.join(", ") }]: see cause for details`; + } + else { + return `[${ errorKeys[0]}]: ${ unformattedErrors[errorKeys[0]] }`; + } + } + return Promise.reject( new Error( - `${ errorDetail } (${ errorPayload.response.status })`, + `${ formatErrors(errors) } (${ errorPayload.response.status })`, { cause } diff --git a/tests/basic.html b/tests/basic.html index 664c7df..8ea564c 100644 --- a/tests/basic.html +++ b/tests/basic.html @@ -47,7 +47,7 @@ } ); - // butter.content.cancelRequest() + butter.content.cancelRequest() butter.post.list({ page: 1, @@ -84,7 +84,7 @@ } ); - //butter.post.cancelRequest() + butter.post.cancelRequest() }); diff --git a/tests/hooks.html b/tests/hooks.html index 648fa3e..d291876 100644 --- a/tests/hooks.html +++ b/tests/hooks.html @@ -58,7 +58,7 @@ } ) .catch( - (error) => console.error("post: ", err.message, err.cause) + (err) => console.error("post: ", err.message, err.cause) ); @@ -67,7 +67,7 @@ (res) => console.log(res) ) .catch( - (error) => console.error("page: ", err.message, err.cause) + (err) => console.error("page: ", err.message, err.cause) ) }); diff --git a/tests/lib/butter.min.js b/tests/lib/butter.min.js index f123da8..b3feb65 100644 --- a/tests/lib/butter.min.js +++ b/tests/lib/butter.min.js @@ -1 +1 @@ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Butter=t():e.Butter=t()}(this,()=>(()=>{"use strict";var n={d:(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},e={};n.d(e,{default:()=>function(e,t={}){if(e)return this instanceof r?r:new r(e,t);throw"ButterCMS API token not set"}});const T="2.0.0",s={author:"authors",category:"categories",content:"content",feed:"feeds",page:"pages",post:"posts",tag:"tags"};function k(e,t){const{onError:r,onRequest:n,onResponse:s,...o}=t.config,{auth_token:a,test:c,preview:i,...u}=t.params;r&&r(e,{options:o,params:u,type:t.type})}function P(e,t,r){t={auth_token:t};return r.testMode&&(t.test=1,t.preview=1),{...e,...t}}function o(R,v){t=R;const y=`https://api.buttercms.com/v2/${s[t]}/`,e=R.replace(R[0],R[0].toUpperCase()),{applyRequestUrlForErrorMessages:q,cancelRequest:w,cleanup:E,determineFetchError:b,signal:j}=function(t){let s=new AbortController,r="";function o(e){return r+": "+e}function a(){s=null}return{applyRequestUrlForErrorMessages:function(e){r=t+` (${e})`},cancelRequest:async function(){await s.abort(o("Request cancelled"))},cleanup:a,controller:s,determineFetchError:function(e,t){var r=s&&s.signal.reason&&s.signal.aborted,n="TimeoutError"===e.name;return r?s.signal.reason:n?o(`Request timed out after ${t}ms.`):(a(),e)},signal:s.signal}}(e);var t;async function r(t=y,r={}){const{apiToken:e,...n}=v,s=new Headers({"Content-Type":"application/json","X-Butter-Client":"JS/"+T});"undefined"==typeof window&&s.append("Accept-Encoding","gzip"),q(t);var{config:r,headers:o,params:a}=await async function(e,t,r){const{onError:n,onRequest:s,onResponse:o,...a}=r.config;var c,i;return s?({headers:e,options:c,params:i}=await s(e,{cancelRequest:r.cancelRequest,headers:r.headers,options:a,params:r.params,type:r.type}),{config:{...c,onError:n,onRequest:s,onResponse:o},headers:e,params:P(i,t,c)}):{...r,params:P(r.params,t,a)}}(t,e,{cancelRequest:w,config:n,headers:s,params:r,type:R});try{const v=AbortSignal.timeout(r.timeout),T=await fetch(t+"?"+new URLSearchParams(a),{cache:r.cache,method:"GET",headers:o,signal:AbortSignal.any([j,v])});if(E(),200!==T.status)throw{response:T,config:r,params:a};{var c=T;var i={config:r,params:a,type:R,requestHeaders:s};const{onError:u,onRequest:p,onResponse:l,...f}=i.config,{auth_token:d,test:m,preview:h,...g}=i.params;if(l){const u=c.clone();await l(u,{options:f,params:g,type:i.type})}return await{data:await c.json(),headers:Object.fromEntries(c.headers.entries()),status:c.status,statusText:c.statusText,config:{url:c.url,method:"get",headers:Object.fromEntries(i.requestHeaders.entries()),transformRequest:p?[p]:[],transformResponse:l?[l]:[],timeout:i.config.timeout}};return await void 0}}catch(e){if(e.response){const T=(await e.response.json())["detail"];for(const[R,T]of e.response.headers.entries())console.log(R+": "+T);const t=Object.fromEntries(Object.entries(e.params).filter(([e])=>"auth_token"!==e)),P=new URL(e.response.url),y=(P.searchParams.delete("auth_token"),{data:T,headers:e.response.headers,status:e.response.status,statusText:e.response.statusText,config:e.config,params:t,type:R,url:P});return k(T,y),Promise.reject(new Error(`${T} (${e.response.status})`,{cause:y}))}{const T=b(e,r.timeout);return k(T,{config:r,params:a,type:R}),E(),console.error("reportableError",T,new Error(T)),Promise.reject(new Error(T))}}}return{cancelRequest:w,list:async function(e={}){return r(y,e)},retrieve:async function(e,t){return r(""+y+e+"/",t)},search:async function(e="",t={}){return t.query=e,r(y+"search/",t)}}}const u={Author:function(e={}){var{cancelRequest:e,list:t,retrieve:r}=o("author",e);return{cancelRequest:e,list:t,retrieve:r}},Category:function(e={}){var{cancelRequest:e,list:t,retrieve:r}=o("category",e);return{cancelRequest:e,list:t,retrieve:r}},Content:function(e={}){var{cancelRequest:e,retrieve:t}=o("content",e);return{cancelRequest:e,retrieve:t}},Feed:function(e={}){var{cancelRequest:e,retrieve:t}=o("feed",e);return{cancelRequest:e,retrieve:t}},Page:function(e={}){const{cancelRequest:t,retrieve:n,search:r}=o("page",e);return{cancelRequest:t,list:async(e,t)=>n(e,t),retrieve:async(e,t,r)=>n(e+"/"+t,r),search:r}},Post:function(e={}){var{cancelRequest:e,list:t,retrieve:r,search:n}=o("post",e);return{cancelRequest:e,list:t,retrieve:r,search:n}},Tag:function(e={}){var{cancelRequest:e,list:t,retrieve:r}=o("tag",e);return{cancelRequest:e,list:t,retrieve:r}}};function r(e,t){var r,n,{cache:t="default",onError:s=null,onRequest:o=null,onResponse:a=null,testMode:c=!1,timeout:i=3e3}=t;return{version:T,...(r=u,n={apiToken:e,cache:t,onError:s,onRequest:o,onResponse:a,testMode:c,timeout:i},Object.keys(r).reduce((e,t)=>({...e,[t.toLocaleLowerCase()]:r[t](n)}),{}))}}return e.default})()); \ No newline at end of file +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Butter=t():e.Butter=t()}(this,()=>(()=>{"use strict";var n={d:(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},e={};n.d(e,{default:()=>function(e,t={}){if(e)return this instanceof r?r:new r(e,t);throw"ButterCMS API token not set"}});const O="2.0.0",s={author:"authors",category:"categories",content:"content",feed:"feeds",page:"pages",post:"posts",tag:"tags"};function P(e,t){const{onError:r,onRequest:n,onResponse:s,...a}=t.config,{auth_token:o,test:c,preview:i,...u}=t.params;r&&r(e,{options:a,params:u,type:t.type})}function x(e,t,r){t={auth_token:t};return r.testMode&&(t.test=1,t.preview=1),{...e,...t}}function a(y,q){t=y;const w=`https://api.buttercms.com/v2/${s[t]}/`,e=y.replace(y[0],y[0].toUpperCase()),{applyRequestUrlForErrorMessages:E,cancelRequest:b,cleanup:j,determineFetchError:k,signal:T}=function(t){let s=new AbortController,r="";function a(e){return r+": "+e}function o(){s=null}return{applyRequestUrlForErrorMessages:function(e){r=t+` (${e})`},cancelRequest:async function(){await s.abort(a("Request cancelled"))},cleanup:o,controller:s,determineFetchError:function(e,t){var r=s&&s.signal.reason&&s.signal.aborted,n="TimeoutError"===e.name;return r?s.signal.reason:n?a(`Request timed out after ${t}ms.`):(o(),e)},signal:s.signal}}(e);var t;async function r(t=w,r={}){const{apiToken:e,...n}=q,s=new Headers({"Content-Type":"application/json","X-Butter-Client":"JS/"+O});"undefined"==typeof window&&s.append("Accept-Encoding","gzip"),E(t);var{config:r,headers:a,params:o}=await async function(e,t,r){const{onError:n,onRequest:s,onResponse:a,...o}=r.config;var c,i;return s?({headers:e,options:c,params:i}=await s(e,{cancelRequest:r.cancelRequest,headers:r.headers,options:o,params:r.params,type:r.type}),{config:{...c,onError:n,onRequest:s,onResponse:a},headers:e,params:x(i,t,c)}):{...r,params:x(r.params,t,o)}}(t,e,{cancelRequest:b,config:n,headers:s,params:r,type:y});try{var c=AbortSignal.timeout(r.timeout),i=await fetch(t+"?"+new URLSearchParams(o),{cache:r.cache,method:"GET",headers:a,signal:AbortSignal.any([T,c])});if(j(),200!==i.status)throw{response:i,config:r,params:o};{var u=i;var p={config:r,params:o,type:y,requestHeaders:s};const{onError:l,onRequest:f,onResponse:d,...h}=p.config,{auth_token:m,test:g,preview:R,...v}=p.params;if(d){const l=u.clone();await d(l,{options:h,params:v,type:p.type})}return await{data:await u.json(),headers:Object.fromEntries(u.headers.entries()),status:u.status,statusText:u.statusText,config:{url:u.url,method:"get",headers:Object.fromEntries(p.requestHeaders.entries()),transformRequest:f?[f]:[],transformResponse:d?[d]:[],timeout:p.config.timeout}};return await void 0}}catch(e){return e.response?(t=await e.response.json(),a=Object.fromEntries(Object.entries(e.params).filter(([e])=>"auth_token"!==e)),(c=new URL(e.response.url)).searchParams.delete("auth_token"),P(t,i={data:t,headers:e.response.headers,status:e.response.status,statusText:e.response.statusText,config:e.config,params:a,type:y,url:c}),Promise.reject(new Error(`${u=t,p=Object.keys(u),1n(e,t),retrieve:async(e,t,r)=>n(e+"/"+t,r),search:r}},Post:function(e={}){var{cancelRequest:e,list:t,retrieve:r,search:n}=a("post",e);return{cancelRequest:e,list:t,retrieve:r,search:n}},Tag:function(e={}){var{cancelRequest:e,list:t,retrieve:r}=a("tag",e);return{cancelRequest:e,list:t,retrieve:r}}};function r(e,t){var r,n,{cache:t="default",onError:s=null,onRequest:a=null,onResponse:o=null,testMode:c=!1,timeout:i=3e3}=t;return{version:O,...(r=u,n={apiToken:e,cache:t,onError:s,onRequest:a,onResponse:o,testMode:c,timeout:i},Object.keys(r).reduce((e,t)=>({...e,[t.toLocaleLowerCase()]:r[t](n)}),{}))}}return e.default})()); \ No newline at end of file