11// Copyright (c) 2022 Developer Innovations, LLC
22
3- import fetch from "node-fetch" ;
3+ import fetch , { Response } from "node-fetch" ;
44import _debug = require( "debug" ) ;
5+ import { gzip } from "zlib" ;
6+ import { promisify } from "util" ;
57
68const debug = _debug ( "unflakable:api" ) ;
79
@@ -34,13 +36,16 @@ export type TestRunRecord = {
3436 name : string [ ] ;
3537 attempts : TestRunAttemptRecord [ ] ;
3638} ;
37- export type CreateTestSuiteRunRequest = {
39+ export type CreateTestSuiteRunInlineRequest = {
3840 branch ?: string ;
3941 commit ?: string ;
4042 start_time : string ;
4143 end_time : string ;
4244 test_runs : TestRunRecord [ ] ;
4345} ;
46+ export declare type CreateTestSuiteRunFromUploadRequest = {
47+ upload_id : string ;
48+ } ;
4449export type TestSuiteRunSummary = {
4550 run_id : string ;
4651 suite_id : string ;
@@ -54,52 +59,113 @@ export type TestSuiteRunSummary = {
5459 num_flake : number ;
5560 num_quarantined : number ;
5661} ;
62+ export type CreateTestSuiteRunUploadUrlResponse = {
63+ upload_id : string ;
64+ } ;
5765
5866const userAgent = ( clientDescription ?: string ) =>
5967 `unflakable-js-api/${ JS_API_VERSION } ${
6068 clientDescription !== undefined ? ` ${ clientDescription } ` : ""
6169 } `;
6270
71+ const requestHeaders = ( {
72+ apiKey,
73+ clientDescription,
74+ } : {
75+ apiKey : string ;
76+ clientDescription ?: string ;
77+ } ) => ( {
78+ Authorization : "Bearer " + apiKey ,
79+ "User-Agent" : userAgent ( clientDescription ) ,
80+ } ) ;
81+
82+ const expectResponse =
83+ ( expectedStatus : number , expectedStatusText : string ) =>
84+ async ( res : Response ) : Promise < Response > => {
85+ if ( res . status !== expectedStatus ) {
86+ const body = await res . text ( ) ;
87+ throw new Error (
88+ `received HTTP response \`${ res . status } ${
89+ res . statusText
90+ } \` (expected \`${ expectedStatusText } \`)${
91+ body . length > 0 ? `: ${ body } ` : ""
92+ } `
93+ ) ;
94+ }
95+ return res ;
96+ } ;
97+
6398export const createTestSuiteRun = async ( {
6499 request,
65100 testSuiteId,
66101 apiKey,
67102 clientDescription,
68103 baseUrl,
69104} : {
70- request : CreateTestSuiteRunRequest ;
105+ request : CreateTestSuiteRunInlineRequest ;
71106 testSuiteId : string ;
72107 apiKey : string ;
73108 clientDescription ?: string ;
74109 baseUrl ?: string ;
75110} ) : Promise < TestSuiteRunSummary > => {
76111 const requestJson = JSON . stringify ( request ) ;
77112 debug ( `Creating test suite run: ${ requestJson } ` ) ;
78- return await fetch (
113+ const gzippedRequest = await promisify ( gzip ) ( requestJson ) ;
114+
115+ const { uploadId, uploadUrl } = await fetch (
79116 `${
80117 baseUrl !== undefined ? baseUrl : BASE_URL
81- } /api/v1/test-suites/${ testSuiteId } /runs`,
118+ } /api/v1/test-suites/${ testSuiteId } /runs/upload `,
82119 {
83120 method : "post" ,
84- body : requestJson ,
85121 headers : {
86- Authorization : "Bearer " + apiKey ,
87122 "Content-Type" : "application/json" ,
88- "User-Agent" : userAgent ( clientDescription ) ,
123+ ... requestHeaders ( { apiKey , clientDescription } ) ,
89124 } ,
90125 }
91126 )
127+ . then ( expectResponse ( 201 , "201 Created" ) )
92128 . then ( async ( res ) => {
93- if ( res . status !== 201 ) {
94- const body = await res . text ( ) ;
95- throw new Error (
96- `received HTTP response \`${ res . status } ${
97- res . statusText
98- } \` (expected \`201 Created\`)${ body . length > 0 ? `: ${ body } ` : "" } `
99- ) ;
129+ const location = res . headers . get ( "Location" ) ;
130+ if ( location === null ) {
131+ throw new Error ( "no Location response header found" ) ;
100132 }
101- return res . json ( ) as Promise < TestSuiteRunSummary > ;
102- } )
133+ const body = ( await res . json ( ) ) as CreateTestSuiteRunUploadUrlResponse ;
134+ return {
135+ uploadId : body . upload_id ,
136+ uploadUrl : location ,
137+ } ;
138+ } ) ;
139+
140+ await fetch ( uploadUrl , {
141+ method : "put" ,
142+ body : gzippedRequest ,
143+ headers : {
144+ "Content-Encoding" : "gzip" ,
145+ "Content-Type" : "application/json" ,
146+ "User-Agent" : userAgent ( clientDescription ) ,
147+ } ,
148+ } ) . then ( expectResponse ( 200 , "200 OK" ) ) ;
149+
150+ const requestBody : CreateTestSuiteRunFromUploadRequest = {
151+ upload_id : uploadId ,
152+ } ;
153+
154+ return await fetch (
155+ `${
156+ baseUrl !== undefined ? baseUrl : BASE_URL
157+ } /api/v1/test-suites/${ testSuiteId } /runs`,
158+ {
159+ method : "post" ,
160+ body : JSON . stringify ( requestBody ) ,
161+ headers : {
162+ "Content-Type" : "application/json" ,
163+ ...requestHeaders ( { apiKey, clientDescription } ) ,
164+ } ,
165+ }
166+ )
167+ . then ( expectResponse ( 201 , "201 Created" ) )
168+ . then ( ( res ) => res . json ( ) as Promise < TestSuiteRunSummary > )
103169 . then ( ( parsedResponse : TestSuiteRunSummary ) => {
104170 debug ( `Received response: ${ JSON . stringify ( parsedResponse ) } ` ) ;
105171 return parsedResponse ;
@@ -124,23 +190,11 @@ export const getTestSuiteManifest = async ({
124190 } /api/v1/test-suites/${ testSuiteId } /manifest`,
125191 {
126192 method : "get" ,
127- headers : {
128- Authorization : "Bearer " + apiKey ,
129- "User-Agent" : userAgent ( clientDescription ) ,
130- } ,
193+ headers : requestHeaders ( { apiKey, clientDescription } ) ,
131194 }
132195 )
133- . then ( async ( res ) => {
134- if ( res . status !== 200 ) {
135- const body = await res . text ( ) ;
136- throw new Error (
137- `received HTTP response \`${ res . status } ${
138- res . statusText
139- } \` (expected \`200 OK\`)${ body . length > 0 ? `: ${ body } ` : "" } `
140- ) ;
141- }
142- return res . json ( ) as Promise < TestSuiteManifest > ;
143- } )
196+ . then ( expectResponse ( 200 , "200 OK" ) )
197+ . then ( ( res ) => res . json ( ) as Promise < TestSuiteManifest > )
144198 . then ( ( parsedResponse : TestSuiteManifest ) => {
145199 debug ( `Received response: ${ JSON . stringify ( parsedResponse ) } ` ) ;
146200 return parsedResponse ;
0 commit comments