@sussudio/platform
Version:
Internal APIs for VS Code's service injection the base services.
817 lines (816 loc) • 29.4 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
var __decorate =
(this && this.__decorate) ||
function (decorators, target, key, desc) {
var c = arguments.length,
r = c < 3 ? target : desc === null ? (desc = Object.getOwnPropertyDescriptor(target, key)) : desc,
d;
if (typeof Reflect === 'object' && typeof Reflect.decorate === 'function')
r = Reflect.decorate(decorators, target, key, desc);
else
for (var i = decorators.length - 1; i >= 0; i--)
if ((d = decorators[i])) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __param =
(this && this.__param) ||
function (paramIndex, decorator) {
return function (target, key) {
decorator(target, key, paramIndex);
};
};
import { createCancelablePromise, timeout } from '@sussudio/base/common/async.mjs';
import { CancellationToken } from '@sussudio/base/common/cancellation.mjs';
import { getErrorMessage, isCancellationError } from '@sussudio/base/common/errors.mjs';
import { Emitter, Event } from '@sussudio/base/common/event.mjs';
import { Disposable, toDisposable } from '@sussudio/base/common/lifecycle.mjs';
import { Mimes } from '@sussudio/base/common/mime.mjs';
import { isWeb } from '@sussudio/base/common/platform.mjs';
import { joinPath, relativePath } from '@sussudio/base/common/resources.mjs';
import { isObject, isString } from '@sussudio/base/common/types.mjs';
import { URI } from '@sussudio/base/common/uri.mjs';
import { generateUuid } from '@sussudio/base/common/uuid.mjs';
import { IConfigurationService } from '../../configuration/common/configuration.mjs';
import { IEnvironmentService } from '../../environment/common/environment.mjs';
import { IFileService } from '../../files/common/files.mjs';
import { IProductService } from '../../product/common/productService.mjs';
import {
asJson,
asText,
asTextOrError,
IRequestService,
isSuccess as isSuccessContext,
} from '../../request/common/request.mjs';
import { getServiceMachineId } from '../../externalServices/common/serviceMachineId.mjs';
import { IStorageService } from '../../storage/common/storage.mjs';
import {
CONFIGURATION_SYNC_STORE_KEY,
HEADER_EXECUTION_ID,
HEADER_OPERATION_ID,
IUserDataSyncLogService,
IUserDataSyncStoreManagementService,
SYNC_SERVICE_URL_TYPE,
UserDataSyncStoreError,
} from './userDataSync.mjs';
const SYNC_PREVIOUS_STORE = 'sync.previous.store';
const DONOT_MAKE_REQUESTS_UNTIL_KEY = 'sync.donot-make-requests-until';
const USER_SESSION_ID_KEY = 'sync.user-session-id';
const MACHINE_SESSION_ID_KEY = 'sync.machine-session-id';
const REQUEST_SESSION_LIMIT = 100;
const REQUEST_SESSION_INTERVAL = 1000 * 60 * 5; /* 5 minutes */
let AbstractUserDataSyncStoreManagementService = class AbstractUserDataSyncStoreManagementService extends Disposable {
productService;
configurationService;
storageService;
_serviceBrand;
_onDidChangeUserDataSyncStore = this._register(new Emitter());
onDidChangeUserDataSyncStore = this._onDidChangeUserDataSyncStore.event;
_userDataSyncStore;
get userDataSyncStore() {
return this._userDataSyncStore;
}
get userDataSyncStoreType() {
return this.storageService.get(SYNC_SERVICE_URL_TYPE, -1 /* StorageScope.APPLICATION */);
}
set userDataSyncStoreType(type) {
this.storageService.store(
SYNC_SERVICE_URL_TYPE,
type,
-1 /* StorageScope.APPLICATION */,
isWeb ? 0 /* StorageTarget.USER */ : 1 /* StorageTarget.MACHINE */,
);
}
constructor(productService, configurationService, storageService) {
super();
this.productService = productService;
this.configurationService = configurationService;
this.storageService = storageService;
this.updateUserDataSyncStore();
this._register(
Event.filter(
storageService.onDidChangeValue,
(e) =>
e.key === SYNC_SERVICE_URL_TYPE &&
e.scope === -1 /* StorageScope.APPLICATION */ &&
this.userDataSyncStoreType !== this.userDataSyncStore?.type,
)(() => this.updateUserDataSyncStore()),
);
}
updateUserDataSyncStore() {
this._userDataSyncStore = this.toUserDataSyncStore(
this.productService[CONFIGURATION_SYNC_STORE_KEY],
this.configurationService.getValue(CONFIGURATION_SYNC_STORE_KEY),
);
this._onDidChangeUserDataSyncStore.fire();
}
toUserDataSyncStore(productStore, configuredStore) {
// Check for web overrides for backward compatibility while reading previous store
productStore = isWeb && productStore?.web ? { ...productStore, ...productStore.web } : productStore;
const value = { ...(productStore || {}), ...(configuredStore || {}) };
if (
value &&
isString(value.url) &&
isObject(value.authenticationProviders) &&
Object.keys(value.authenticationProviders).every((authenticationProviderId) =>
Array.isArray(value.authenticationProviders[authenticationProviderId].scopes),
)
) {
const syncStore = value;
const canSwitch = !!syncStore.canSwitch && !configuredStore?.url;
const defaultType = syncStore.url === syncStore.insidersUrl ? 'insiders' : 'stable';
const type = (canSwitch ? this.userDataSyncStoreType : undefined) || defaultType;
const url =
configuredStore?.url ||
(type === 'insiders' ? syncStore.insidersUrl : type === 'stable' ? syncStore.stableUrl : syncStore.url);
return {
url: URI.parse(url),
type,
defaultType,
defaultUrl: URI.parse(syncStore.url),
stableUrl: URI.parse(syncStore.stableUrl),
insidersUrl: URI.parse(syncStore.insidersUrl),
canSwitch,
authenticationProviders: Object.keys(syncStore.authenticationProviders).reduce((result, id) => {
result.push({ id, scopes: syncStore.authenticationProviders[id].scopes });
return result;
}, []),
};
}
return undefined;
}
};
AbstractUserDataSyncStoreManagementService = __decorate(
[__param(0, IProductService), __param(1, IConfigurationService), __param(2, IStorageService)],
AbstractUserDataSyncStoreManagementService,
);
export { AbstractUserDataSyncStoreManagementService };
let UserDataSyncStoreManagementService = class UserDataSyncStoreManagementService extends AbstractUserDataSyncStoreManagementService {
previousConfigurationSyncStore;
constructor(productService, configurationService, storageService) {
super(productService, configurationService, storageService);
const previousConfigurationSyncStore = this.storageService.get(
SYNC_PREVIOUS_STORE,
-1 /* StorageScope.APPLICATION */,
);
if (previousConfigurationSyncStore) {
this.previousConfigurationSyncStore = JSON.parse(previousConfigurationSyncStore);
}
const syncStore = this.productService[CONFIGURATION_SYNC_STORE_KEY];
if (syncStore) {
this.storageService.store(
SYNC_PREVIOUS_STORE,
JSON.stringify(syncStore),
-1 /* StorageScope.APPLICATION */,
1 /* StorageTarget.MACHINE */,
);
} else {
this.storageService.remove(SYNC_PREVIOUS_STORE, -1 /* StorageScope.APPLICATION */);
}
}
async switch(type) {
if (type !== this.userDataSyncStoreType) {
this.userDataSyncStoreType = type;
this.updateUserDataSyncStore();
}
}
async getPreviousUserDataSyncStore() {
return this.toUserDataSyncStore(this.previousConfigurationSyncStore);
}
};
UserDataSyncStoreManagementService = __decorate(
[__param(0, IProductService), __param(1, IConfigurationService), __param(2, IStorageService)],
UserDataSyncStoreManagementService,
);
export { UserDataSyncStoreManagementService };
let UserDataSyncStoreClient = class UserDataSyncStoreClient extends Disposable {
requestService;
logService;
storageService;
userDataSyncStoreUrl;
authToken;
commonHeadersPromise;
session;
_onTokenFailed = this._register(new Emitter());
onTokenFailed = this._onTokenFailed.event;
_onTokenSucceed = this._register(new Emitter());
onTokenSucceed = this._onTokenSucceed.event;
_donotMakeRequestsUntil = undefined;
get donotMakeRequestsUntil() {
return this._donotMakeRequestsUntil;
}
_onDidChangeDonotMakeRequestsUntil = this._register(new Emitter());
onDidChangeDonotMakeRequestsUntil = this._onDidChangeDonotMakeRequestsUntil.event;
constructor(
userDataSyncStoreUrl,
productService,
requestService,
logService,
environmentService,
fileService,
storageService,
) {
super();
this.requestService = requestService;
this.logService = logService;
this.storageService = storageService;
this.updateUserDataSyncStoreUrl(userDataSyncStoreUrl);
this.commonHeadersPromise = getServiceMachineId(environmentService, fileService, storageService).then((uuid) => {
const headers = {
'X-Client-Name': `${productService.applicationName}${isWeb ? '-web' : ''}`,
'X-Client-Version': productService.version,
};
if (productService.commit) {
headers['X-Client-Commit'] = productService.commit;
}
return headers;
});
/* A requests session that limits requests per sessions */
this.session = new RequestsSession(
REQUEST_SESSION_LIMIT,
REQUEST_SESSION_INTERVAL,
this.requestService,
this.logService,
);
this.initDonotMakeRequestsUntil();
this._register(
toDisposable(() => {
if (this.resetDonotMakeRequestsUntilPromise) {
this.resetDonotMakeRequestsUntilPromise.cancel();
this.resetDonotMakeRequestsUntilPromise = undefined;
}
}),
);
}
setAuthToken(token, type) {
this.authToken = { token, type };
}
updateUserDataSyncStoreUrl(userDataSyncStoreUrl) {
this.userDataSyncStoreUrl = userDataSyncStoreUrl ? joinPath(userDataSyncStoreUrl, 'v1') : undefined;
}
initDonotMakeRequestsUntil() {
const donotMakeRequestsUntil = this.storageService.getNumber(
DONOT_MAKE_REQUESTS_UNTIL_KEY,
-1 /* StorageScope.APPLICATION */,
);
if (donotMakeRequestsUntil && Date.now() < donotMakeRequestsUntil) {
this.setDonotMakeRequestsUntil(new Date(donotMakeRequestsUntil));
}
}
resetDonotMakeRequestsUntilPromise = undefined;
setDonotMakeRequestsUntil(donotMakeRequestsUntil) {
if (this._donotMakeRequestsUntil?.getTime() !== donotMakeRequestsUntil?.getTime()) {
this._donotMakeRequestsUntil = donotMakeRequestsUntil;
if (this.resetDonotMakeRequestsUntilPromise) {
this.resetDonotMakeRequestsUntilPromise.cancel();
this.resetDonotMakeRequestsUntilPromise = undefined;
}
if (this._donotMakeRequestsUntil) {
this.storageService.store(
DONOT_MAKE_REQUESTS_UNTIL_KEY,
this._donotMakeRequestsUntil.getTime(),
-1 /* StorageScope.APPLICATION */,
1 /* StorageTarget.MACHINE */,
);
this.resetDonotMakeRequestsUntilPromise = createCancelablePromise((token) =>
timeout(this._donotMakeRequestsUntil.getTime() - Date.now(), token).then(() =>
this.setDonotMakeRequestsUntil(undefined),
),
);
this.resetDonotMakeRequestsUntilPromise.then(null, (e) => null /* ignore error */);
} else {
this.storageService.remove(DONOT_MAKE_REQUESTS_UNTIL_KEY, -1 /* StorageScope.APPLICATION */);
}
this._onDidChangeDonotMakeRequestsUntil.fire();
}
}
// #region Collection
async getAllCollections(headers = {}) {
if (!this.userDataSyncStoreUrl) {
throw new Error('No settings sync store url configured.');
}
const url = joinPath(this.userDataSyncStoreUrl, 'collection').toString();
headers = { ...headers };
headers['Content-Type'] = 'application/json';
const context = await this.request(url, { type: 'GET', headers }, [], CancellationToken.None);
return (await asJson(context))?.map(({ id }) => id) || [];
}
async createCollection(headers = {}) {
if (!this.userDataSyncStoreUrl) {
throw new Error('No settings sync store url configured.');
}
const url = joinPath(this.userDataSyncStoreUrl, 'collection').toString();
headers = { ...headers };
headers['Content-Type'] = Mimes.text;
const context = await this.request(url, { type: 'POST', headers }, [], CancellationToken.None);
const collectionId = await asTextOrError(context);
if (!collectionId) {
throw new UserDataSyncStoreError(
'Server did not return the collection id',
url,
'NoCollection' /* UserDataSyncErrorCode.NoCollection */,
context.res.statusCode,
context.res.headers[HEADER_OPERATION_ID],
);
}
return collectionId;
}
async deleteCollection(collection, headers = {}) {
if (!this.userDataSyncStoreUrl) {
throw new Error('No settings sync store url configured.');
}
const url = collection
? joinPath(this.userDataSyncStoreUrl, 'collection', collection).toString()
: joinPath(this.userDataSyncStoreUrl, 'collection').toString();
headers = { ...headers };
await this.request(url, { type: 'DELETE', headers }, [], CancellationToken.None);
}
// #endregion
// #region Resource
async getAllResourceRefs(resource, collection) {
if (!this.userDataSyncStoreUrl) {
throw new Error('No settings sync store url configured.');
}
const uri = this.getResourceUrl(this.userDataSyncStoreUrl, collection, resource);
const headers = {};
const context = await this.request(uri.toString(), { type: 'GET', headers }, [], CancellationToken.None);
const result = (await asJson(context)) || [];
return result.map(({ url, created }) => ({
ref: relativePath(uri, uri.with({ path: url })),
created: created * 1000 /* Server returns in seconds */,
}));
}
async resolveResourceContent(resource, ref, collection, headers = {}) {
if (!this.userDataSyncStoreUrl) {
throw new Error('No settings sync store url configured.');
}
const url = joinPath(this.getResourceUrl(this.userDataSyncStoreUrl, collection, resource), ref).toString();
headers = { ...headers };
headers['Cache-Control'] = 'no-cache';
const context = await this.request(url, { type: 'GET', headers }, [], CancellationToken.None);
const content = await asTextOrError(context);
return content;
}
async deleteResource(resource, ref, collection) {
if (!this.userDataSyncStoreUrl) {
throw new Error('No settings sync store url configured.');
}
const url =
ref !== null
? joinPath(this.getResourceUrl(this.userDataSyncStoreUrl, collection, resource), ref).toString()
: this.getResourceUrl(this.userDataSyncStoreUrl, collection, resource).toString();
const headers = {};
await this.request(url, { type: 'DELETE', headers }, [], CancellationToken.None);
}
async deleteResources() {
if (!this.userDataSyncStoreUrl) {
throw new Error('No settings sync store url configured.');
}
const url = joinPath(this.userDataSyncStoreUrl, 'resource').toString();
const headers = { 'Content-Type': Mimes.text };
await this.request(url, { type: 'DELETE', headers }, [], CancellationToken.None);
}
async readResource(resource, oldValue, collection, headers = {}) {
if (!this.userDataSyncStoreUrl) {
throw new Error('No settings sync store url configured.');
}
const url = joinPath(this.getResourceUrl(this.userDataSyncStoreUrl, collection, resource), 'latest').toString();
headers = { ...headers };
// Disable caching as they are cached by synchronisers
headers['Cache-Control'] = 'no-cache';
if (oldValue) {
headers['If-None-Match'] = oldValue.ref;
}
const context = await this.request(url, { type: 'GET', headers }, [304], CancellationToken.None);
let userData = null;
if (context.res.statusCode === 304) {
userData = oldValue;
}
if (userData === null) {
const ref = context.res.headers['etag'];
if (!ref) {
throw new UserDataSyncStoreError(
'Server did not return the ref',
url,
'NoRef' /* UserDataSyncErrorCode.NoRef */,
context.res.statusCode,
context.res.headers[HEADER_OPERATION_ID],
);
}
const content = await asTextOrError(context);
if (!content && context.res.statusCode === 304) {
throw new UserDataSyncStoreError(
'Empty response',
url,
'EmptyResponse' /* UserDataSyncErrorCode.EmptyResponse */,
context.res.statusCode,
context.res.headers[HEADER_OPERATION_ID],
);
}
userData = { ref, content };
}
return userData;
}
async writeResource(resource, data, ref, collection, headers = {}) {
if (!this.userDataSyncStoreUrl) {
throw new Error('No settings sync store url configured.');
}
const url = this.getResourceUrl(this.userDataSyncStoreUrl, collection, resource).toString();
headers = { ...headers };
headers['Content-Type'] = Mimes.text;
if (ref) {
headers['If-Match'] = ref;
}
const context = await this.request(url, { type: 'POST', data, headers }, [], CancellationToken.None);
const newRef = context.res.headers['etag'];
if (!newRef) {
throw new UserDataSyncStoreError(
'Server did not return the ref',
url,
'NoRef' /* UserDataSyncErrorCode.NoRef */,
context.res.statusCode,
context.res.headers[HEADER_OPERATION_ID],
);
}
return newRef;
}
// #endregion
async manifest(oldValue, headers = {}) {
if (!this.userDataSyncStoreUrl) {
throw new Error('No settings sync store url configured.');
}
const url = joinPath(this.userDataSyncStoreUrl, 'manifest').toString();
headers = { ...headers };
headers['Content-Type'] = 'application/json';
if (oldValue) {
headers['If-None-Match'] = oldValue.ref;
}
const context = await this.request(url, { type: 'GET', headers }, [304], CancellationToken.None);
let manifest = null;
if (context.res.statusCode === 304) {
manifest = oldValue;
}
if (!manifest) {
const ref = context.res.headers['etag'];
if (!ref) {
throw new UserDataSyncStoreError(
'Server did not return the ref',
url,
'NoRef' /* UserDataSyncErrorCode.NoRef */,
context.res.statusCode,
context.res.headers[HEADER_OPERATION_ID],
);
}
const content = await asTextOrError(context);
if (!content && context.res.statusCode === 304) {
throw new UserDataSyncStoreError(
'Empty response',
url,
'EmptyResponse' /* UserDataSyncErrorCode.EmptyResponse */,
context.res.statusCode,
context.res.headers[HEADER_OPERATION_ID],
);
}
if (content) {
manifest = { ...JSON.parse(content), ref };
}
}
const currentSessionId = this.storageService.get(USER_SESSION_ID_KEY, -1 /* StorageScope.APPLICATION */);
if (currentSessionId && manifest && currentSessionId !== manifest.session) {
// Server session is different from client session so clear cached session.
this.clearSession();
}
if (manifest === null && currentSessionId) {
// server session is cleared so clear cached session.
this.clearSession();
}
if (manifest) {
// update session
this.storageService.store(
USER_SESSION_ID_KEY,
manifest.session,
-1 /* StorageScope.APPLICATION */,
1 /* StorageTarget.MACHINE */,
);
}
return manifest;
}
async clear() {
if (!this.userDataSyncStoreUrl) {
throw new Error('No settings sync store url configured.');
}
await this.deleteCollection();
await this.deleteResources();
// clear cached session.
this.clearSession();
}
getResourceUrl(userDataSyncStoreUrl, collection, resource) {
return collection
? joinPath(userDataSyncStoreUrl, 'collection', collection, 'resource', resource)
: joinPath(userDataSyncStoreUrl, 'resource', resource);
}
clearSession() {
this.storageService.remove(USER_SESSION_ID_KEY, -1 /* StorageScope.APPLICATION */);
this.storageService.remove(MACHINE_SESSION_ID_KEY, -1 /* StorageScope.APPLICATION */);
}
async request(url, options, successCodes, token) {
if (!this.authToken) {
throw new UserDataSyncStoreError(
'No Auth Token Available',
url,
'Unauthorized' /* UserDataSyncErrorCode.Unauthorized */,
undefined,
undefined,
);
}
if (this._donotMakeRequestsUntil && Date.now() < this._donotMakeRequestsUntil.getTime()) {
throw new UserDataSyncStoreError(
`${options.type} request '${url}' failed because of too many requests (429).`,
url,
'TooManyRequestsAndRetryAfter' /* UserDataSyncErrorCode.TooManyRequestsAndRetryAfter */,
undefined,
undefined,
);
}
this.setDonotMakeRequestsUntil(undefined);
const commonHeaders = await this.commonHeadersPromise;
options.headers = {
...(options.headers || {}),
...commonHeaders,
'X-Account-Type': this.authToken.type,
authorization: `Bearer ${this.authToken.token}`,
};
// Add session headers
this.addSessionHeaders(options.headers);
this.logService.trace('Sending request to server', {
url,
type: options.type,
headers: { ...options.headers, ...{ authorization: undefined } },
});
let context;
try {
context = await this.session.request(url, options, token);
} catch (e) {
if (!(e instanceof UserDataSyncStoreError)) {
let code = 'RequestFailed'; /* UserDataSyncErrorCode.RequestFailed */
const errorMessage = getErrorMessage(e).toLowerCase();
// Request timed out
if (errorMessage.includes('xhr timeout')) {
code = 'RequestTimeout' /* UserDataSyncErrorCode.RequestTimeout */;
}
// Request protocol not supported
else if (errorMessage.includes('protocol') && errorMessage.includes('not supported')) {
code = 'RequestProtocolNotSupported' /* UserDataSyncErrorCode.RequestProtocolNotSupported */;
}
// Request path not escaped
else if (errorMessage.includes('request path contains unescaped characters')) {
code = 'RequestPathNotEscaped' /* UserDataSyncErrorCode.RequestPathNotEscaped */;
}
// Request header not an object
else if (errorMessage.includes('headers must be an object')) {
code = 'RequestHeadersNotObject' /* UserDataSyncErrorCode.RequestHeadersNotObject */;
}
// Request canceled
else if (isCancellationError(e)) {
code = 'RequestCanceled' /* UserDataSyncErrorCode.RequestCanceled */;
}
e = new UserDataSyncStoreError(`Connection refused for the request '${url}'.`, url, code, undefined, undefined);
}
this.logService.info('Request failed', url);
throw e;
}
const operationId = context.res.headers[HEADER_OPERATION_ID];
const requestInfo = {
url,
status: context.res.statusCode,
'execution-id': options.headers[HEADER_EXECUTION_ID],
'operation-id': operationId,
};
const isSuccess =
isSuccessContext(context) || (context.res.statusCode && successCodes.indexOf(context.res.statusCode) !== -1);
let failureMessage = '';
if (isSuccess) {
this.logService.trace('Request succeeded', requestInfo);
} else {
this.logService.info('Request failed', requestInfo);
failureMessage = (await asText(context)) || '';
}
if (context.res.statusCode === 401) {
this.authToken = undefined;
this._onTokenFailed.fire();
throw new UserDataSyncStoreError(
`Request '${url}' failed because of Unauthorized (401).`,
url,
'Unauthorized' /* UserDataSyncErrorCode.Unauthorized */,
context.res.statusCode,
operationId,
);
}
this._onTokenSucceed.fire();
if (context.res.statusCode === 404) {
throw new UserDataSyncStoreError(
`${options.type} request '${url}' failed because the requested resource is not found (404).`,
url,
'NotFound' /* UserDataSyncErrorCode.NotFound */,
context.res.statusCode,
operationId,
);
}
if (context.res.statusCode === 405) {
throw new UserDataSyncStoreError(
`${options.type} request '${url}' failed because the requested endpoint is not found (405). ${failureMessage}`,
url,
'MethodNotFound' /* UserDataSyncErrorCode.MethodNotFound */,
context.res.statusCode,
operationId,
);
}
if (context.res.statusCode === 409) {
throw new UserDataSyncStoreError(
`${options.type} request '${url}' failed because of Conflict (409). There is new data for this resource. Make the request again with latest data.`,
url,
'Conflict' /* UserDataSyncErrorCode.Conflict */,
context.res.statusCode,
operationId,
);
}
if (context.res.statusCode === 410) {
throw new UserDataSyncStoreError(
`${options.type} request '${url}' failed because the requested resource is not longer available (410).`,
url,
'Gone' /* UserDataSyncErrorCode.Gone */,
context.res.statusCode,
operationId,
);
}
if (context.res.statusCode === 412) {
throw new UserDataSyncStoreError(
`${options.type} request '${url}' failed because of Precondition Failed (412). There is new data for this resource. Make the request again with latest data.`,
url,
'PreconditionFailed' /* UserDataSyncErrorCode.PreconditionFailed */,
context.res.statusCode,
operationId,
);
}
if (context.res.statusCode === 413) {
throw new UserDataSyncStoreError(
`${options.type} request '${url}' failed because of too large payload (413).`,
url,
'TooLarge' /* UserDataSyncErrorCode.TooLarge */,
context.res.statusCode,
operationId,
);
}
if (context.res.statusCode === 426) {
throw new UserDataSyncStoreError(
`${options.type} request '${url}' failed with status Upgrade Required (426). Please upgrade the client and try again.`,
url,
'UpgradeRequired' /* UserDataSyncErrorCode.UpgradeRequired */,
context.res.statusCode,
operationId,
);
}
if (context.res.statusCode === 429) {
const retryAfter = context.res.headers['retry-after'];
if (retryAfter) {
this.setDonotMakeRequestsUntil(new Date(Date.now() + parseInt(retryAfter) * 1000));
throw new UserDataSyncStoreError(
`${options.type} request '${url}' failed because of too many requests (429).`,
url,
'TooManyRequestsAndRetryAfter' /* UserDataSyncErrorCode.TooManyRequestsAndRetryAfter */,
context.res.statusCode,
operationId,
);
} else {
throw new UserDataSyncStoreError(
`${options.type} request '${url}' failed because of too many requests (429).`,
url,
'RemoteTooManyRequests' /* UserDataSyncErrorCode.TooManyRequests */,
context.res.statusCode,
operationId,
);
}
}
if (!isSuccess) {
throw new UserDataSyncStoreError(
'Server returned ' + context.res.statusCode,
url,
'Unknown' /* UserDataSyncErrorCode.Unknown */,
context.res.statusCode,
operationId,
);
}
return context;
}
addSessionHeaders(headers) {
let machineSessionId = this.storageService.get(MACHINE_SESSION_ID_KEY, -1 /* StorageScope.APPLICATION */);
if (machineSessionId === undefined) {
machineSessionId = generateUuid();
this.storageService.store(
MACHINE_SESSION_ID_KEY,
machineSessionId,
-1 /* StorageScope.APPLICATION */,
1 /* StorageTarget.MACHINE */,
);
}
headers['X-Machine-Session-Id'] = machineSessionId;
const userSessionId = this.storageService.get(USER_SESSION_ID_KEY, -1 /* StorageScope.APPLICATION */);
if (userSessionId !== undefined) {
headers['X-User-Session-Id'] = userSessionId;
}
}
};
UserDataSyncStoreClient = __decorate(
[
__param(1, IProductService),
__param(2, IRequestService),
__param(3, IUserDataSyncLogService),
__param(4, IEnvironmentService),
__param(5, IFileService),
__param(6, IStorageService),
],
UserDataSyncStoreClient,
);
export { UserDataSyncStoreClient };
let UserDataSyncStoreService = class UserDataSyncStoreService extends UserDataSyncStoreClient {
_serviceBrand;
constructor(
userDataSyncStoreManagementService,
productService,
requestService,
logService,
environmentService,
fileService,
storageService,
) {
super(
userDataSyncStoreManagementService.userDataSyncStore?.url,
productService,
requestService,
logService,
environmentService,
fileService,
storageService,
);
this._register(
userDataSyncStoreManagementService.onDidChangeUserDataSyncStore(() =>
this.updateUserDataSyncStoreUrl(userDataSyncStoreManagementService.userDataSyncStore?.url),
),
);
}
};
UserDataSyncStoreService = __decorate(
[
__param(0, IUserDataSyncStoreManagementService),
__param(1, IProductService),
__param(2, IRequestService),
__param(3, IUserDataSyncLogService),
__param(4, IEnvironmentService),
__param(5, IFileService),
__param(6, IStorageService),
],
UserDataSyncStoreService,
);
export { UserDataSyncStoreService };
export class RequestsSession {
limit;
interval;
requestService;
logService;
requests = [];
startTime = undefined;
constructor(limit, interval, /* in ms */ requestService, logService) {
this.limit = limit;
this.interval = interval;
this.requestService = requestService;
this.logService = logService;
}
request(url, options, token) {
if (this.isExpired()) {
this.reset();
}
options.url = url;
if (this.requests.length >= this.limit) {
this.logService.info('Too many requests', ...this.requests);
throw new UserDataSyncStoreError(
`Too many requests. Only ${this.limit} requests allowed in ${this.interval / (1000 * 60)} minutes.`,
url,
'LocalTooManyRequests' /* UserDataSyncErrorCode.LocalTooManyRequests */,
undefined,
undefined,
);
}
this.startTime = this.startTime || new Date();
this.requests.push(url);
return this.requestService.request(options, token);
}
isExpired() {
return this.startTime !== undefined && new Date().getTime() - this.startTime.getTime() > this.interval;
}
reset() {
this.requests = [];
this.startTime = undefined;
}
}