Skip to content

Commit

Permalink
updated to make multiple HTTP requests simultaneously
Browse files Browse the repository at this point in the history
  • Loading branch information
imolorhe committed Dec 16, 2023
1 parent 7f5e651 commit b2021f6
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 70 deletions.
38 changes: 13 additions & 25 deletions packages/altair-app/src/app/modules/altair/effects/query.effect.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,15 @@
import {
of as observableOf,
EMPTY,
Observable,
iif,
Subscriber,
of,
from,
combineLatest,
zip,
} from 'rxjs';
import { of as observableOf, EMPTY, of, from, combineLatest } from 'rxjs';

import {
tap,
catchError,
withLatestFrom,
switchMap,
map,
takeUntil,
distinct,
mergeMap,
} from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { Store, Action } from '@ngrx/store';
import { Store } from '@ngrx/store';
import { Actions, ofType, createEffect } from '@ngrx/effects';

import {
Expand All @@ -31,11 +19,9 @@ import {
DonationService,
ElectronAppService,
EnvironmentService,
PreRequestService,
SubscriptionProviderRegistryService,
QueryService,
} from '../services';
import * as fromRoot from '../store';

import * as queryActions from '../store/query/query.action';
import * as variablesActions from '../store/variables/variables.action';
Expand All @@ -60,7 +46,6 @@ import { debug } from '../utils/logger';
import { generateCurl } from '../utils/curl';
import { OperationDefinitionNode } from 'graphql';
import { IDictionary, UnknownError } from '../interfaces/shared';
import { SendRequestResponse } from '../services/gql/gql.service';
import { RootState } from 'altair-graphql-core/build/types/state/state.interfaces';
import { WEBSOCKET_PROVIDER_ID } from 'altair-graphql-core/build/subscriptions';
import { SubscriptionProvider } from 'altair-graphql-core/build/subscriptions/subscription-provider';
Expand All @@ -72,6 +57,7 @@ import { RequestType } from '../services/pre-request/helpers';
export class QueryEffects {
// Sends the query request to the specified URL
// with the specified headers and variables
// NOTE: Should use mergeMap instead of switchMap, because switchMap cancels the previous request
sendQueryRequest$ = createEffect(
() => {
return this.actions$.pipe(
Expand All @@ -90,22 +76,22 @@ export class QueryEffects {
};
}
),
switchMap((response) => {
mergeMap((response) => {
if (response.action.type === queryActions.CANCEL_QUERY_REQUEST) {
this.store.dispatch(
new layoutActions.StopLoadingAction(response.windowId)
);
return EMPTY;
}

const query = (response.data?.query.query || '').trim();
const query = (response.data?.query.query ?? '').trim();
if (!query) {
return EMPTY;
}

return observableOf(response);
}),
switchMap((response) => {
mergeMap((response) => {
return combineLatest([
of(response),
from(
Expand All @@ -117,13 +103,13 @@ export class QueryEffects {
})
);
}),
switchMap((returnedData) => {
mergeMap((returnedData) => {
if (!returnedData) {
return EMPTY;
}

return observableOf(returnedData).pipe(
switchMap((_returnedData) => {
mergeMap((_returnedData) => {
const { response, transformedData } = _returnedData;

if (!response.data) {
Expand Down Expand Up @@ -263,7 +249,8 @@ export class QueryEffects {

debug.log('Sending..');
return this.gqlService
.sendRequest(url, {
.sendRequest({
url,
query,
variables,
headers,
Expand Down Expand Up @@ -555,7 +542,8 @@ export class QueryEffects {
new docsAction.StartLoadingDocsAction(response.windowId)
);
return this.gqlService
.getIntrospectionRequest(url, {
.getIntrospectionRequest({
url,
method: response.data.query.httpVerb,
headers,
withCredentials:
Expand Down Expand Up @@ -939,7 +927,7 @@ export class QueryEffects {
}

const subscriptionProviderId =
response.data.query.subscriptionProviderId || WEBSOCKET_PROVIDER_ID;
response.data.query.subscriptionProviderId ?? WEBSOCKET_PROVIDER_ID;
const { getProviderClass } =
this.subscriptionProviderRegistryService.getProviderData(
subscriptionProviderId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ describe('GqlService', () => {
it('should call HttpClient with expected parameters', inject(
[GqlService],
(service: GqlService) => {
service.sendRequest('http://test.com', {
service.sendRequest({
url: 'http://test.com',
method: 'post',
query: '{}',
});
Expand All @@ -162,7 +163,8 @@ describe('GqlService', () => {
it('should call HttpClient with correct content type when file is included', inject(
[GqlService],
(service: GqlService) => {
service.sendRequest('http://test.com', {
service.sendRequest({
url: 'http://test.com',
method: 'post',
query: '{}',
files: [
Expand Down Expand Up @@ -197,7 +199,8 @@ describe('GqlService', () => {
it('should call HttpClient with default json content type if file passed to it is not valid', inject(
[GqlService],
(service: GqlService) => {
service.sendRequest('http://test.com', {
service.sendRequest({
url: 'http://test.com',
method: 'post',
query: '{}',
files: [
Expand Down Expand Up @@ -254,7 +257,8 @@ describe('GqlService', () => {
}
};
const res = await service
.getIntrospectionRequest('http://test.com', {
.getIntrospectionRequest({
url: 'http://test.com',
method: 'GET',
})
.pipe(take(1))
Expand Down Expand Up @@ -298,7 +302,8 @@ describe('GqlService', () => {
}
};
const res = await service
.getIntrospectionRequest('http://test.com', {
.getIntrospectionRequest({
url: 'http://test.com',
method: 'GET',
})
.pipe(take(1))
Expand Down Expand Up @@ -342,7 +347,8 @@ describe('GqlService', () => {

try {
const res = await service
.getIntrospectionRequest('http://test.com', {
.getIntrospectionRequest({
url: 'http://test.com',
method: 'GET',
})
.pipe(take(1))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import { ElectronAppService } from '../electron-app/electron-app.service';
import { ELECTRON_ALLOWED_FORBIDDEN_HEADERS } from '@altairgraphql/electron-interop/build/constants';

interface SendRequestOptions {
url: string;
query: string;
method: string;
withCredentials?: boolean;
Expand Down Expand Up @@ -91,9 +92,6 @@ export class GqlService {
headers = new HttpHeaders();
introspectionData = {};

private api_url = localStorage.getItem('altair:url');
private method = 'POST';

constructor(
private http: HttpClient,
private notifyService: NotifyService,
Expand All @@ -103,28 +101,17 @@ export class GqlService {
this.setHeaders();
}

sendRequest(
url: string,
opts: SendRequestOptions
): Observable<SendRequestResponse> {
sendRequest(opts: SendRequestOptions): Observable<SendRequestResponse> {
// Only need resolvedFiles to know if valid files exist at this point
const { resolvedFiles } = this.normalizeFiles(opts.files);

this.setUrl(url)
.setHTTPMethod(opts.method)
// Skip json default headers for files
.setHeaders(opts.headers, {
skipDefaults: this.isGETRequest(opts.method) || !!resolvedFiles.length,
});
// Skip json default headers for files
this.setHeaders(opts.headers, {
skipDefaults: this.isGETRequest(opts.method) || !!resolvedFiles.length,
});

const requestStartTime = new Date().getTime();
return this._send({
query: opts.query,
variables: opts.variables,
selectedOperation: opts.selectedOperation,
files: opts.files,
withCredentials: opts.withCredentials,
}).pipe(
return this._send(opts).pipe(
map((response) => {
const requestEndTime = new Date().getTime();
const requestElapsedTime = requestEndTime - requestStartTime;
Expand All @@ -147,7 +134,7 @@ export class GqlService {
);
}

isGETRequest(method = this.method) {
private isGETRequest(method: string) {
return method.toLowerCase() === 'get';
}

Expand Down Expand Up @@ -190,26 +177,17 @@ export class GqlService {
}, new HttpParams());
}

setUrl(url: string) {
this.api_url = url;
return this;
}

setHTTPMethod(httpVerb: string) {
this.method = httpVerb;
return this;
}

getIntrospectionRequest(url: string, opts: IntrospectionRequestOptions) {
getIntrospectionRequest(opts: IntrospectionRequestOptions) {
const requestOpts: SendRequestOptions = {
url: opts.url,
query: getIntrospectionQuery(),
headers: opts.headers,
method: opts.method,
withCredentials: opts.withCredentials,
variables: '{}',
selectedOperation: 'IntrospectionQuery',
};
return this.sendRequest(url, requestOpts).pipe(
return this.sendRequest(requestOpts).pipe(
map((data) => {
debug.log('introspection', data.response);
if (!data.response.ok) {
Expand All @@ -223,7 +201,7 @@ export class GqlService {
debug.log('Error from first introspection query.', err);

// Try the old introspection query
return this.sendRequest(url, {
return this.sendRequest({
...requestOpts,
query: oldIntrospectionQuery,
}).pipe(
Expand Down Expand Up @@ -624,12 +602,14 @@ export class GqlService {
* @param vars
*/
private _send({
url,
method,
query,
variables,
selectedOperation,
files,
withCredentials,
}: Omit<SendRequestOptions, 'method'>) {
}: SendRequestOptions) {
const data = {
query,
variables: {},
Expand All @@ -654,7 +634,7 @@ export class GqlService {
}
}

if (!this.isGETRequest()) {
if (!this.isGETRequest(method)) {
const { resolvedFiles } = this.normalizeFiles(files);
if (resolvedFiles && resolvedFiles.length) {
// https://github.com/jaydenseric/graphql-multipart-request-spec#multipart-form-field-structure
Expand All @@ -678,13 +658,13 @@ export class GqlService {
} else {
params = this.getParamsFromData(data);
}
if (!this.api_url) {
if (!url) {
throw new Error('You need to have a URL for the request!');
}
return this.http
.request(this.method, this.api_url, {
.request(method, url, {
// GET method uses params, while the other methods use body
...(!this.isGETRequest() && { body }),
...(!this.isGETRequest(method) && { body }),
params,
headers,
observe: 'response',
Expand Down

0 comments on commit b2021f6

Please sign in to comment.