Skip to content

Commit

Permalink
Merge d8671b3 into 1624782
Browse files Browse the repository at this point in the history
  • Loading branch information
samisayegh committed Oct 21, 2020
2 parents 1624782 + d8671b3 commit 3a4b233
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 227 deletions.
137 changes: 4 additions & 133 deletions src/rest/EndpointCaller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@ import { Assert } from '../misc/Assert';
import { TimeSpan } from '../utils/TimeSpanUtils';
import { DeviceUtils } from '../utils/DeviceUtils';
import { Utils } from '../utils/Utils';
import { JQueryUtils } from '../utils/JQueryutils';
import * as _ from 'underscore';
import { UrlUtils } from '../utils/UrlUtils';

declare const XDomainRequest;

export interface IEndpointCaller {
call<T>(params: IEndpointCallParameters): Promise<ISuccessResponse<T>>;
options: IEndpointCallerOptions;
Expand Down Expand Up @@ -181,28 +178,13 @@ enum XMLHttpRequestStatus {
/**
* This class is in charge of calling an endpoint (eg: a {@link SearchEndpoint}).
*
* This means it's only uses to execute an XMLHttpRequest (for example), massage the response and check if there are errors.
* This means it's only used to execute an XMLHttpRequest, massage the response and check if there are errors.
*
* Will execute the call and return a Promise.
*
* Call using one of those options :
*
* * XMLHttpRequest for recent browser that support CORS, or if the endpoint is on the same origin.
* * XDomainRequest for older IE browser that do not support CORS.
* * Jsonp if all else fails, or is explicitly enabled.
*/
* */
export class EndpointCaller implements IEndpointCaller {
public logger: Logger;

/**
* Set this property to true to enable Jsonp call to the endpoint.<br/>
* Be aware that jsonp is "easier" to setup endpoint wise, but has a lot of drawback and limitation for the client code.<br/>
* Default to false.
* @type {boolean}
*/
public useJsonp = false;

private static JSONP_ERROR_TIMEOUT = 10000;
/**
* Create a new EndpointCaller.
* @param options Specify the authentication that will be used for this endpoint. Not needed if the endpoint is public and has no authentication
Expand Down Expand Up @@ -232,10 +214,7 @@ export class EndpointCaller implements IEndpointCaller {

/**
* Generic call to the endpoint using the provided {@link IEndpointCallParameters}.<br/>
* Internally, will decide which method to use to call the endpoint :<br/>
* -- XMLHttpRequest for recent browser that support CORS, or if the endpoint is on the same origin.<br/>
* -- XDomainRequest for older IE browser that do not support CORS.<br/>
* -- Jsonp if all else fails, or is explicitly enabled.
* Internally, will use XMLHttpRequest to call the endpoint :<br/>
* @param params The parameters to use for the call
* @returns {any} A promise of the given type
*/
Expand All @@ -255,24 +234,7 @@ export class EndpointCaller implements IEndpointCaller {
}

this.logger.trace('Performing REST request', requestInfo);
const urlObject = this.parseURL(requestInfo.url);
// In IE8, hostname and port return "" when we are on the same domain.
const isLocalHost = window.location.hostname === urlObject.hostname || urlObject.hostname === '';

const currentPort = window.location.port != '' ? window.location.port : window.location.protocol == 'https:' ? '443' : '80';
const isSamePort = currentPort == urlObject.port;
const isCrossOrigin = !(isLocalHost && isSamePort);
if (!this.useJsonp) {
if (this.isCORSSupported() || !isCrossOrigin) {
return this.callUsingXMLHttpRequest(requestInfo, params.responseType);
} else if (this.isXDomainRequestSupported()) {
return this.callUsingXDomainRequest(requestInfo);
} else {
return this.callUsingAjaxJsonP(requestInfo);
}
} else {
return this.callUsingAjaxJsonP(requestInfo);
}
return this.callUsingXMLHttpRequest(requestInfo, params.responseType);
}

/**
Expand Down Expand Up @@ -371,89 +333,6 @@ export class EndpointCaller implements IEndpointCaller {
});
}

/**
* Call the endpoint using XDomainRequest https://msdn.microsoft.com/en-us/library/cc288060(v=vs.85).aspx<br/>
* Used for IE8/9
* @param requestInfo The info about the request
* @returns {Promise<T>|Promise}
*/
public callUsingXDomainRequest<T>(requestInfo: IRequestInfo<T>): Promise<ISuccessResponse<T>> {
return new Promise((resolve, reject) => {
let queryString = requestInfo.queryString.concat([]);

// XDomainRequest don't support including stuff in the header, so we must
// put the access token in the query string if we have one.
if (this.options.accessToken) {
queryString.push('access_token=' + Utils.safeEncodeURIComponent(this.options.accessToken));
}

const xDomainRequest = new XDomainRequest();
if (requestInfo.method == 'GET') {
queryString = queryString.concat(EndpointCaller.convertJsonToQueryString(requestInfo.requestData));
}
xDomainRequest.open(requestInfo.method, this.combineUrlAndQueryString(requestInfo.url, queryString));

xDomainRequest.onload = () => {
const data = this.tryParseResponseText(xDomainRequest.responseText, xDomainRequest.contentType);
this.handleSuccessfulResponseThatMightBeAnError(requestInfo, data, resolve, reject);
};

xDomainRequest.onerror = () => {
const data = this.tryParseResponseText(xDomainRequest.responseText, xDomainRequest.contentType);
this.handleError(requestInfo, 0, data, reject);
};

// We must set those functions otherwise it will sometime fail in IE
xDomainRequest.ontimeout = () => this.logger.error('Request timeout', xDomainRequest, requestInfo.requestData);
xDomainRequest.onprogress = () => this.logger.trace('Request progress', xDomainRequest, requestInfo.requestData);

// We must open the request in a separate thread, for obscure reasons
_.defer(() => {
if (requestInfo.method == 'GET') {
xDomainRequest.send();
} else {
xDomainRequest.send(EndpointCaller.convertJsonToFormBody(requestInfo.requestData));
}
});
});
}

/**
* Call the endpoint using Jsonp https://en.wikipedia.org/wiki/JSONP<br/>
* Should be used for dev only, or for very special setup as using jsonp has a lot of drawbacks.
* @param requestInfo The info about the request
* @returns {Promise<T>|Promise}
*/
public callUsingAjaxJsonP<T>(requestInfo: IRequestInfo<T>): Promise<ISuccessResponse<T>> {
let jQuery = JQueryUtils.getJQuery();
Assert.check(jQuery, 'Using jsonp without having included jQuery is not supported.');
return new Promise((resolve, reject) => {
const queryString = requestInfo.queryString.concat(EndpointCaller.convertJsonToQueryString(requestInfo.requestData));

// JSONP don't support including stuff in the header, so we must
// put the access token in the query string if we have one.
if (this.options.accessToken) {
queryString.push('access_token=' + Utils.safeEncodeURIComponent(this.options.accessToken));
}

queryString.push('callback=?');

jQuery.ajax({
url: this.combineUrlAndQueryString(requestInfo.url, queryString),
dataType: 'jsonp',
success: (data: any) => this.handleSuccessfulResponseThatMightBeAnError(requestInfo, data, resolve, reject),
timeout: EndpointCaller.JSONP_ERROR_TIMEOUT,
error: () => this.handleError(requestInfo, 0, undefined, reject)
});
});
}

private parseURL(url: string) {
const urlObject = document.createElement('a');
urlObject.href = url;
return urlObject;
}

private getXmlHttpRequest(): XMLHttpRequest {
const newXmlHttpRequest = this.options.xmlHttpRequest || XMLHttpRequest;
return new newXmlHttpRequest();
Expand Down Expand Up @@ -493,14 +372,6 @@ export class EndpointCaller implements IEndpointCaller {
});
}

private isXDomainRequestSupported(): boolean {
return 'XDomainRequest' in window;
}

private isCORSSupported(): boolean {
return 'withCredentials' in this.getXmlHttpRequest();
}

private isSuccessHttpStatus(status: number): boolean {
return (status >= 200 && status < 300) || status === 304;
}
Expand Down
43 changes: 22 additions & 21 deletions src/rest/SearchEndpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,11 +323,12 @@ export class SearchEndpoint implements ISearchEndpoint {
}

/**
* @deprecated jsonP is no longer supported. This method will always return `false`.
* Indicates whether the search endpoint is using JSONP internally to communicate with the Search API.
* @returns {boolean} `true` in the search enpoint is using JSONP; `false` otherwise.
*/
public isJsonp(): boolean {
return this.caller.useJsonp;
return false;
}

@includeActionsHistory()
Expand Down Expand Up @@ -1276,10 +1277,10 @@ function defaultDecoratorEndpointCallParameters() {
}

function path(path: string) {
return function (target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {
return function(target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {
const { originalMethod, nbParams } = decoratorSetup(target, key, descriptor);

descriptor.value = function (...args: any[]) {
descriptor.value = function(...args: any[]) {
const url = this.buildBaseUri(path);
if (args[nbParams - 1]) {
args[nbParams - 1].url = url;
Expand All @@ -1295,10 +1296,10 @@ function path(path: string) {
}

function alertsPath(path: string) {
return function (target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {
return function(target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {
const { originalMethod, nbParams } = decoratorSetup(target, key, descriptor);

descriptor.value = function (...args: any[]) {
descriptor.value = function(...args: any[]) {
const url = this.buildSearchAlertsUri(path);
if (args[nbParams - 1]) {
args[nbParams - 1].url = url;
Expand All @@ -1314,10 +1315,10 @@ function alertsPath(path: string) {
}

function requestDataType(type: string) {
return function (target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {
return function(target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {
const { originalMethod, nbParams } = decoratorSetup(target, key, descriptor);

descriptor.value = function (...args: any[]) {
descriptor.value = function(...args: any[]) {
if (args[nbParams - 1]) {
args[nbParams - 1].requestDataType = type;
} else {
Expand All @@ -1331,10 +1332,10 @@ function requestDataType(type: string) {
}

function method(met: string) {
return function (target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {
return function(target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {
const { originalMethod, nbParams } = decoratorSetup(target, key, descriptor);

descriptor.value = function (...args: any[]) {
descriptor.value = function(...args: any[]) {
if (args[nbParams - 1]) {
args[nbParams - 1].method = met;
} else {
Expand All @@ -1349,10 +1350,10 @@ function method(met: string) {
}

function responseType(resp: string) {
return function (target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {
return function(target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {
const { originalMethod, nbParams } = decoratorSetup(target, key, descriptor);

descriptor.value = function (...args: any[]) {
descriptor.value = function(...args: any[]) {
if (args[nbParams - 1]) {
args[nbParams - 1].responseType = resp;
} else {
Expand All @@ -1367,7 +1368,7 @@ function responseType(resp: string) {
}

function accessTokenInUrl(tokenKey: string = 'access_token') {
return function (target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {
return function(target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {
const { originalMethod, nbParams } = decoratorSetup(target, key, descriptor);
const buildAccessToken = (tokenKey: string, endpointInstance: SearchEndpoint): string[] => {
let queryString: string[] = [];
Expand All @@ -1379,7 +1380,7 @@ function accessTokenInUrl(tokenKey: string = 'access_token') {
return queryString;
};

descriptor.value = function (...args: any[]) {
descriptor.value = function(...args: any[]) {
const queryString = buildAccessToken(tokenKey, this);
if (args[nbParams - 1]) {
args[nbParams - 1].queryString = args[nbParams - 1].queryString.concat(queryString);
Expand All @@ -1395,10 +1396,10 @@ function accessTokenInUrl(tokenKey: string = 'access_token') {
}

function includeActionsHistory(historyStore = buildHistoryStore()) {
return function (target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {
return function(target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {
const { originalMethod, nbParams } = decoratorSetup(target, key, descriptor);

descriptor.value = function (...args: any[]) {
descriptor.value = function(...args: any[]) {
let historyFromStore = historyStore.getHistory();
if (historyFromStore == null) {
historyFromStore = [];
Expand All @@ -1420,9 +1421,9 @@ function includeActionsHistory(historyStore = buildHistoryStore()) {
}

function includeReferrer() {
return function (target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {
return function(target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {
const { originalMethod, nbParams } = decoratorSetup(target, key, descriptor);
descriptor.value = function (...args: any[]) {
descriptor.value = function(...args: any[]) {
let referrer = document.referrer;
if (referrer == null) {
referrer = '';
Expand All @@ -1444,9 +1445,9 @@ function includeReferrer() {
}

function includeVisitorId() {
return function (target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {
return function(target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {
const { originalMethod, nbParams } = decoratorSetup(target, key, descriptor);
descriptor.value = function (...args: any[]) {
descriptor.value = function(...args: any[]) {
let visitorId = Cookie.get('visitorId');
if (visitorId == null) {
visitorId = '';
Expand All @@ -1468,9 +1469,9 @@ function includeVisitorId() {
}

function includeIsGuestUser() {
return function (target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {
return function(target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {
const { originalMethod, nbParams } = decoratorSetup(target, key, descriptor);
descriptor.value = function (...args: any[]) {
descriptor.value = function(...args: any[]) {
let isGuestUser = this.options.isGuestUser;

if (args[nbParams - 1]) {
Expand Down
3 changes: 2 additions & 1 deletion src/rest/SearchEndpointInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,9 @@ export interface ISearchEndpoint {
options?: ISearchEndpointOptions;
getBaseUri(): string;
getBaseAlertsUri(): string;
getAuthenticationProviderUri(provider: string, returnUri: string, message: string): string;
/** @deprecated jsonP is no longer supported. This method will always return `false`. */
isJsonp(): boolean;
getAuthenticationProviderUri(provider: string, returnUri: string, message: string): string;
search(query: IQuery, callOptions?: IEndpointCallOptions): Promise<IQueryResults>;
fetchBinary(query: IQuery, callOptions?: IEndpointCallOptions): Promise<ArrayBuffer>;
plan(query: IQuery, callOptions?: IEndpointCallOptions): Promise<ExecutionPlan>;
Expand Down
20 changes: 3 additions & 17 deletions src/ui/Thumbnail/Thumbnail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,27 +115,13 @@ export class Thumbnail extends Component {
private buildThumbnailImage() {
let endpoint = this.bindings.queryController.getEndpoint();

if (endpoint.isJsonp()) {
// For jsonp we can't GET/POST for binary data. We are limited
// to only setting the src attribute directly on the img.
this.buildImageWithDirectSrcAttribute(endpoint);
} else {
// Base 64 img allows us to GET/POST the image as raw binary, so that we can also
// pass the credential of the user. Useful for phonegap among others.
this.buildImageWithBase64SrcAttribute(endpoint);
}
// Base 64 img allows us to GET/POST the image as raw binary, so that we can also
// pass the credential of the user. Useful for phonegap among others.
this.buildImageWithBase64SrcAttribute(endpoint);

this.makeAccessible();
}

private buildImageWithDirectSrcAttribute(endpoint: ISearchEndpoint) {
let dataStreamUri = endpoint.getViewAsDatastreamUri(this.result.uniqueId, '$Thumbnail$', {
contentType: 'image/png'
});
this.img.setAttribute('src', dataStreamUri);
this.resizeContainingFieldTable();
}

private buildImageWithBase64SrcAttribute(endpoint: ISearchEndpoint) {
endpoint
.getRawDataStream(this.result.uniqueId, '$Thumbnail$')
Expand Down
12 changes: 4 additions & 8 deletions src/utils/HtmlUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,14 +104,10 @@ export class ImageUtils {
options = options ? options : <IImageUtilsOptions>{};

let img = ImageUtils.buildImage(undefined, _.extend(options, { 'data-coveo-uri-hash': result.raw['urihash'] }));
if (endpoint.isJsonp()) {
// For jsonp we can't GET/POST for binary data. We are limited to only setting the src attribute directly on the img.
ImageUtils.buildImageWithDirectSrcAttribute(endpoint, result);
} else {
// Base 64 img allows us to GET/POST the image as raw binary, so that we can also pass the credential of the user
// Useful for phonegap.
ImageUtils.buildImageWithBase64SrcAttribute(endpoint, result);
}
// Base 64 img allows us to GET/POST the image as raw binary, so that we can also pass the credential of the user
// Useful for phonegap.
ImageUtils.buildImageWithBase64SrcAttribute(endpoint, result);

return img;
}
}
Loading

0 comments on commit 3a4b233

Please sign in to comment.