11
11
*/
12
12
// @ts -check
13
13
14
- import { errorResponse , makeContext } from './util.js' ;
14
+ import { errorResponse , errorWithResponse , makeContext } from './util.js' ;
15
15
import getProductQueryCS from './queries/cs-product.js' ;
16
16
import getProductQueryCore from './queries/core-product.js' ;
17
17
import HTML_TEMPLATE from './templates/html.js' ;
18
+ import { resolveConfig } from './config.js' ;
18
19
19
20
/**
20
- * @type {Record<string, Config> }
21
- */
22
- const TENANT_CONFIGS = {
23
- visualcomfort : {
24
- apiKey : '59878b5d8af24fe9a354f523f5a0bb62' ,
25
- magentoEnvironmentId : '97034e45-43a5-48ab-91ab-c9b5a98623a8' ,
26
- magentoWebsiteCode : 'base' ,
27
- magentoStoreViewCode : 'default' ,
28
- coreEndpoint : 'https://www.visualcomfort.com/graphql' ,
29
- } ,
30
- } ;
31
-
32
- /**
33
- * @param {string } tenant
34
- * @param {Partial<Config> } [overrides={}]
35
- * @returns {Config|null }
36
- */
37
- function lookupConfig ( tenant , overrides ) {
38
- if ( ! TENANT_CONFIGS [ tenant ] ) {
39
- return null ;
40
- }
41
- // @ts -ignore
42
- return {
43
- ...TENANT_CONFIGS [ tenant ] ,
44
- ...overrides ,
45
- } ;
46
- }
47
-
48
- /**
49
- * @param {string } sku
50
- * @param {Config } config
51
- */
21
+ * @param {string } sku
22
+ * @param {Config } config
23
+ */
52
24
async function fetchProductCS ( sku , config ) {
53
25
const query = getProductQueryCS ( { sku } ) ;
54
26
@@ -62,32 +34,32 @@ async function fetchProductCS(sku, config) {
62
34
} ,
63
35
} ) ;
64
36
if ( ! resp . ok ) {
65
- console . warn ( 'failed to fetch product: ' , resp . status ) ;
66
- return resp ;
37
+ console . warn ( 'failed to fetch product: ' , resp . status , resp . statusText ) ;
38
+ throw errorWithResponse ( resp . status , 'failed to fetch product' ) ;
67
39
}
68
40
69
41
const json = await resp . json ( ) ;
70
42
try {
71
43
const [ product ] = json . data . products ;
72
44
if ( ! product ) {
73
- return errorResponse ( 404 , 'could not find product' , json . errors ) ;
45
+ throw errorWithResponse ( 404 , 'could not find product' , json . errors ) ;
74
46
}
75
47
return product ;
76
48
} catch ( e ) {
77
49
console . error ( 'failed to parse product: ' , e ) ;
78
- return errorResponse ( 500 , 'failed to parse product response' ) ;
50
+ throw errorWithResponse ( 500 , 'failed to parse product response' ) ;
79
51
}
80
52
}
81
53
82
54
/**
83
- * @param {{ urlKey : string } | { sku: string } } opt
55
+ * @param {{ urlkey : string } | { sku: string } } opt
84
56
* @param {Config } config
85
57
*/
86
58
// eslint-disable-next-line no-unused-vars
87
59
async function fetchProductCore ( opt , config ) {
88
60
const query = getProductQueryCore ( opt ) ;
89
61
if ( ! config . coreEndpoint ) {
90
- return errorResponse ( 400 , 'coreEndpoint not configured' ) ;
62
+ throw errorWithResponse ( 400 , 'coreEndpoint not configured' ) ;
91
63
}
92
64
93
65
const resp = await fetch ( `${ config . coreEndpoint } ?query=${ encodeURIComponent ( query ) } ` , {
@@ -100,47 +72,36 @@ async function fetchProductCore(opt, config) {
100
72
} ,
101
73
} ) ;
102
74
if ( ! resp . ok ) {
103
- console . warn ( 'failed to fetch product: ' , resp . status ) ;
104
- return resp ;
75
+ console . warn ( 'failed to fetch product: ' , resp . status , resp . statusText ) ;
76
+ throw errorWithResponse ( resp . status , 'failed to fetch product' ) ;
105
77
}
106
78
107
79
const json = await resp . json ( ) ;
108
80
try {
109
81
const [ product ] = json . data . products ;
110
82
if ( ! product ) {
111
- return errorResponse ( 404 , 'could not find product' , json . errors ) ;
83
+ throw errorWithResponse ( 404 , 'could not find product' , json . errors ) ;
112
84
}
113
85
return product ;
114
86
} catch ( e ) {
115
87
console . error ( 'failed to parse product: ' , e ) ;
116
- return errorResponse ( 500 , 'failed to parse product response' ) ;
88
+ throw errorWithResponse ( 500 , 'failed to parse product response' ) ;
117
89
}
118
90
}
119
91
120
- function resolvePDPTemplate ( product ) {
121
- return HTML_TEMPLATE ( product ) ;
122
- }
123
-
124
92
/**
125
- * @param {Context } ctx
126
- */
127
- async function handlePDPRequest ( ctx ) {
128
- // TODO: pull from config
129
- // eslint-disable-next-line no-unused-vars
130
- const [ _ , tenant , _route , _geo , _pageType , urlKey , sku ] = ctx . url . pathname . split ( '/' ) ;
131
- if ( ! sku ) {
132
- return errorResponse ( 404 , 'missing sku' ) ;
133
- }
134
-
135
- const overrides = Object . fromEntries ( ctx . url . searchParams . entries ( ) ) ;
136
- const config = lookupConfig ( tenant , overrides ) ;
137
- if ( ! config ) {
138
- return errorResponse ( 404 , 'config not found' ) ;
93
+ * @param {Context } ctx
94
+ * @param {Config } config
95
+ */
96
+ async function handlePDPRequest ( ctx , config ) {
97
+ const { sku, urlkey } = config . params ;
98
+ if ( ! sku && ! urlkey ) {
99
+ return errorResponse ( 404 , 'missing sku or urlkey' ) ;
139
100
}
140
101
141
102
// const product = await fetchProductCore({ sku }, config);
142
103
const product = await fetchProductCS ( sku . toUpperCase ( ) , config ) ;
143
- const html = resolvePDPTemplate ( product ) ;
104
+ const html = HTML_TEMPLATE ( product ) ;
144
105
return new Response ( html , {
145
106
status : 200 ,
146
107
headers : {
@@ -150,38 +111,54 @@ async function handlePDPRequest(ctx) {
150
111
}
151
112
152
113
/**
153
- * @param {Context } ctx
154
- */
155
- async function handleContentRequest ( ctx ) {
156
- const [ pageType ] = ctx . url . pathname . split ( '/' ) . slice ( 4 ) ;
157
- switch ( pageType ) {
158
- case 'product' :
159
- return handlePDPRequest ( ctx ) ;
160
- default :
161
- return errorResponse ( 404 , 'unknown content subroute' ) ;
162
- }
163
- }
114
+ * @type {Record<string, (ctx: Context, config: Config) => Promise<Response>> }
115
+ */
116
+ const handlers = {
117
+ content : async ( ctx , config ) => {
118
+ if ( config . pageType !== 'product' ) {
119
+ return errorResponse ( 404 , 'page type not supported' ) ;
120
+ }
121
+ return handlePDPRequest ( ctx , config ) ;
122
+ } ,
123
+ // eslint-disable-next-line no-unused-vars
124
+ graphql : async ( ctx , config ) => errorResponse ( 501 , 'not implemented' ) ,
125
+ } ;
164
126
165
127
export default {
166
128
/**
167
- *
168
- * @param {Request } request
169
- * @param {Record<string, string> } env
170
- * @param {import("@cloudflare/workers-types/experimental").ExecutionContext } pctx
171
- * @returns {Promise<Response> }
172
- */
129
+ * @param {Request } request
130
+ * @param {Record<string, string> } env
131
+ * @param {import("@cloudflare/workers-types/experimental").ExecutionContext } pctx
132
+ * @returns {Promise<Response> }
133
+ */
173
134
async fetch ( request , env , pctx ) {
174
135
const ctx = makeContext ( pctx , request , env ) ;
136
+ if ( ctx . info . method !== 'GET' ) {
137
+ return errorResponse ( 405 , 'method not allowed' ) ;
138
+ }
139
+
175
140
const [ _ , tenant , route ] = ctx . url . pathname . split ( '/' ) ;
176
141
if ( ! tenant ) {
177
- return errorResponse ( 400 , 'missing tenant' ) ;
142
+ return errorResponse ( 404 , 'missing tenant' ) ;
143
+ }
144
+ if ( ! route ) {
145
+ return errorResponse ( 404 , 'missing route' ) ;
146
+ }
147
+
148
+ const overrides = Object . fromEntries ( ctx . url . searchParams . entries ( ) ) ;
149
+ const config = resolveConfig ( ctx , tenant , overrides ) ;
150
+ if ( ! config ) {
151
+ return errorResponse ( 404 , 'config not found' ) ;
178
152
}
179
153
180
- switch ( route ) {
181
- case 'content' :
182
- return handleContentRequest ( ctx ) ;
183
- default :
184
- return errorResponse ( 404 , 'no route found' ) ;
154
+ try {
155
+ return handlers [ route ] ( ctx , config ) ;
156
+ } catch ( e ) {
157
+ if ( e . response ) {
158
+ return e . response ;
159
+ }
160
+ ctx . log . error ( e ) ;
161
+ return errorResponse ( 500 , 'internal server error' ) ;
185
162
}
186
163
} ,
187
164
} ;
0 commit comments