@@ -22,9 +22,18 @@ import _ajvErrors from "ajv-errors";
2222import _ajvFormats from "ajv-formats" ;
2323import type { OpenAPIV3_1 } from "openapi-types" ;
2424
25+ import type { DiscourseExecOptions } from "./types.ts" ;
26+ export * from "./types.ts" ;
27+
2528import spec from "./openapi.json" with { type : "json" } ;
2629import DiscourseAPIGenerated from "./generated.ts" ;
2730
31+ const execOptionDefaults : DiscourseExecOptions = {
32+ fetchOptions : { } ,
33+ validateParams : true ,
34+ // validateResponse: true,
35+ } ;
36+
2837// Previously we imported this from ./generated.ts, but, exporting it
2938// from there breaks the unwrapping.
3039type Prettify < T > =
@@ -181,9 +190,21 @@ export class HTTPError extends Error {
181190 url : string ,
182191 requestInit : RequestInit ,
183192 ) {
193+ const ri = {
194+ ...requestInit ,
195+ headers : requestInit . headers instanceof Headers
196+ ? Object . fromEntries ( requestInit . headers . entries ( ) )
197+ : requestInit . headers as
198+ | Record < string , string | string [ ] | undefined >
199+ | undefined ,
200+ } ;
201+ if ( ri . headers ?. [ "api-key" ] ) {
202+ ri . headers [ "api-key" ] = ri . headers [ "api-key" ] . slice ( 0 , 7 ) + "..." ;
203+ }
204+
184205 super (
185206 `An error occured calling "${ operationName } " at ${ url } \n` +
186- `RequestInit: ${ JSON . stringify ( requestInit ) } \n` +
207+ `RequestInit: ${ JSON . stringify ( ri ) } \n` +
187208 `Returned HTTP ${ status } : ${ body } ` ,
188209 ) ;
189210 this . status = status ;
@@ -251,10 +272,13 @@ export default class DiscourseAPI extends DiscourseAPIGenerated {
251272 override async _exec < T > (
252273 operationName : string ,
253274 params = { } as Record < string , string > ,
275+ options : DiscourseExecOptions = { } ,
254276 ) : Promise < unknown > {
255277 const operation = byOperationId [ operationName ] ;
256278 if ( ! operation ) throw new Error ( "Unknown operation: " + operationName ) ;
257279
280+ const opts = { ...execOptionDefaults , ...options } ;
281+
258282 // console.log(operationName, params);
259283
260284 const header : { [ key : string ] : string } = { } ;
@@ -352,22 +376,40 @@ export default class DiscourseAPI extends DiscourseAPIGenerated {
352376
353377 const additionalProperties = Object . keys ( params ) ;
354378 if ( additionalProperties . length ) {
355- throw new Error (
356- "Unknown parameter(s) for " +
357- operationName +
358- ": " +
359- additionalProperties . join ( ", " ) ,
360- ) ;
379+ if ( opts . validateParams ) {
380+ throw new Error (
381+ "Unknown parameter(s) for " +
382+ operationName +
383+ ": " +
384+ additionalProperties . join ( ", " ) ,
385+ ) ;
386+ } else {
387+ if ( formData ) {
388+ for ( const key of additionalProperties ) {
389+ formData . append ( key , params [ key ] as string | Blob ) ;
390+ }
391+ } else if ( contentType === "application/json" ) {
392+ for ( const key of additionalProperties ) {
393+ body [ key ] = params [ key ] ;
394+ }
395+ } else {
396+ throw new Error (
397+ "additionalProperties but not sure where to put them" ,
398+ ) ;
399+ }
400+ }
361401 }
362402
363- const validate = getValidator ( operationSchema ( operation . data ) ) ;
364- const valid = validate ( { header, path, query, body } ) ;
365- if ( ! valid ) {
366- const errors = validate . errors ;
367- // console.log("errors", errors);
368- const message = ajv . errorsText ( errors ) ;
369- const error = new ParamaterValidationError ( message , errors ) ;
370- throw error ;
403+ if ( opts . validateParams ) {
404+ const validate = getValidator ( operationSchema ( operation . data ) ) ;
405+ const valid = validate ( { header, path, query, body } ) ;
406+ if ( ! valid ) {
407+ const errors = validate . errors ;
408+ // console.log("errors", errors);
409+ const message = ajv . errorsText ( errors ) ;
410+ const error = new ParamaterValidationError ( message , errors ) ;
411+ throw error ;
412+ }
371413 }
372414
373415 // Do this after validation (of user headers)
@@ -396,6 +438,7 @@ export default class DiscourseAPI extends DiscourseAPIGenerated {
396438 method : operation . method ,
397439 headers : new Headers ( header ) ,
398440 redirect : "manual" ,
441+ ...opts . fetchOptions ,
399442 } ;
400443
401444 if ( operation . data . requestBody && "content" in operation . data . requestBody ) {
@@ -481,30 +524,33 @@ export default class DiscourseAPI extends DiscourseAPIGenerated {
481524 file ?: File | Blob ;
482525 }
483526 > ,
527+ options ?: Prettify < DiscourseExecOptions > ,
484528 ) : Prettify < ReturnType < DiscourseAPIGenerated [ "createUpload" ] > > {
485529 const file = params . file ;
486530 if ( file && ! ( file instanceof File || file instanceof Blob ) ) {
487531 throw new Error ( "file must be a File or Blob, not " + typeof file ) ;
488532 }
489533
490534 // @ts -expect-error: intentional break of types
491- return super . createUpload ( params ) ;
535+ return super . createUpload ( params , options ) ;
492536 }
493537
494538 override updateTopic (
495539 params : Parameters < DiscourseAPIGenerated [ "updateTopic" ] > [ 0 ] ,
540+ options ?: Prettify < DiscourseExecOptions > ,
496541 ) : ReturnType < DiscourseAPIGenerated [ "updateTopic" ] > {
497542 console . warn (
498543 "At time of writing, updateTopic() is broken on the discourse side, see https://meta.discourse.org/t/stumped-on-api-update-of-topic/145330." ,
499544 ) ;
500- return super . updateTopic ( params ) ;
545+ return super . updateTopic ( params , options ) ;
501546 }
502547
503548 override async getTopicByExternalId (
504549 params : Parameters < DiscourseAPIGenerated [ "getTopicByExternalId" ] > [ 0 ] ,
550+ options ?: Prettify < DiscourseExecOptions > ,
505551 ) : ReturnType < DiscourseAPIGenerated [ "getTopic" ] > {
506552 try {
507- await super . getTopicByExternalId ( params ) ;
553+ await super . getTopicByExternalId ( params , options ) ;
508554 } catch ( error ) {
509555 if ( error instanceof HTTPError && error . status === 301 ) {
510556 const location = error . response . headers . get ( "location" ) ;
@@ -525,6 +571,7 @@ export default class DiscourseAPI extends DiscourseAPIGenerated {
525571
526572 override getSpecificPostsFromTopic (
527573 params : Parameters < DiscourseAPIGenerated [ "getSpecificPostsFromTopic" ] > [ 0 ] ,
574+ options ?: Prettify < DiscourseExecOptions > ,
528575 ) : ReturnType < DiscourseAPIGenerated [ "getSpecificPostsFromTopic" ] > {
529576 const operation = byOperationId . getSpecificPostsFromTopic . data ;
530577
@@ -555,7 +602,7 @@ export default class DiscourseAPI extends DiscourseAPIGenerated {
555602 delete operation . requestBody ;
556603 }
557604
558- return super . getSpecificPostsFromTopic ( params ) ;
605+ return super . getSpecificPostsFromTopic ( params , options ) ;
559606 }
560607
561608 /*
0 commit comments