Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(@aws-amplify/analytics)!: do not attempt to delete unused endpoints #7245

Merged
merged 2 commits into from
Nov 20, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -749,33 +749,17 @@ describe('AnalyticsProvider test', () => {
// Reject with error the first time we execute updateEndpoint
.mockImplementationOnce(async params => {
throw mockExceededMaxError;
})
// Succeed on the second attempt (i.e. after we go through _retryEndpointUpdate)
.mockImplementationOnce(async params => {
return 'data';
});

jest
.spyOn(Credentials, 'get')
.mockImplementationOnce(() => Promise.resolve(credentials));

jest
.spyOn(AnalyticsProvider.prototype, '_removeUnusedEndpoints')
.mockImplementationOnce(() => ({
promise: jest.fn().mockResolvedValue(true),
}));

const spyonRetryEndpointUpdate = jest.spyOn(
AnalyticsProvider.prototype,
'_retryEndpointUpdate'
);

const params = { event: { name: '_update_endpoint', immediate: true } };

await analytics.record(params, { resolve, reject });

expect(spyonUpdateEndpoint).toHaveBeenCalledTimes(2); // 1 failed + 1 successful call
expect(spyonRetryEndpointUpdate).toHaveBeenCalled();
expect(spyonUpdateEndpoint).toHaveBeenCalledTimes(1); // 1 failed + 1 successful call
iartemiev marked this conversation as resolved.
Show resolved Hide resolved

spyonUpdateEndpoint.mockRestore();
});
Expand Down
104 changes: 3 additions & 101 deletions packages/analytics/src/Providers/AWSPinpointProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import {
ConsoleLogger as Logger,
ClientDevice,
Platform,
Credentials,
Signer,
JS,
Expand All @@ -26,7 +25,6 @@ import {
PutEventsCommand,
PutEventsCommandInput,
UpdateEndpointCommand,
GetUserEndpointsCommand,
} from '@aws-sdk/client-pinpoint';
import { EventsBatch } from '@aws-sdk/client-pinpoint/models';
import Cache from '@aws-amplify/cache';
Expand Down Expand Up @@ -55,7 +53,6 @@ const logger = new Logger('AWSPinpointProvider');
const RETRYABLE_CODES = [429, 500];
const ACCEPTED_CODES = [202];
const FORBIDDEN_CODE = 403;
const BAD_REQUEST_CODE = 400;
const MOBILE_SERVICE_NAME = 'mobiletargeting';
const EXPIRED_TOKEN_CODE = 'ExpiredTokenException';
const UPDATE_ENDPOINT = '_update_endpoint';
Expand Down Expand Up @@ -433,11 +430,9 @@ export class AWSPinpointProvider implements AnalyticsProvider {
const { err, endpointObject } = failureData;
const statusCode = err.$metadata && err.$metadata.httpStatusCode;

logger.debug('updateEndpoint failed', err);
logger.debug('updateEndpoint error', err);

switch (statusCode) {
case BAD_REQUEST_CODE:
return this._handleEndpointUpdateBadRequest(failureData);
case FORBIDDEN_CODE:
return this._handleEndpointUpdateForbidden(failureData);
default:
Expand All @@ -446,41 +441,11 @@ export class AWSPinpointProvider implements AnalyticsProvider {
const exponential = true;
return this._retryEndpointUpdate(endpointObject, exponential);
}
logger.error('updateEndpoint failed', err);
endpointObject.handlers.reject(err);
}
}

private async _handleEndpointUpdateBadRequest(
failureData: EndpointFailureData
) {
const { err, update_params, endpointObject } = failureData;
const { message } = err;
const { ApplicationId, EndpointRequest } = update_params;

if (
!String(message).startsWith('Exceeded maximum endpoint per user count')
) {
return endpointObject.handlers.reject(err);
}

try {
await this._removeUnusedEndpoints(
ApplicationId,
EndpointRequest.User.UserId
);
logger.debug('Removed unused endpoints successfully');
this._retryEndpointUpdate(endpointObject);
} catch (err) {
logger.warn(`Failed to remove unused endpoints with error: ${err}`);
logger.warn(
`Please ensure you have updated your Pinpoint IAM Policy ` +
`with the Action: "mobiletargeting:GetUserEndpoints" ` +
`in order to get endpoints info of the user`
);
return endpointObject.handlers.reject(err);
}
}

private _handleEndpointUpdateForbidden(failureData: EndpointFailureData) {
const { err, endpointObject } = failureData;

Expand Down Expand Up @@ -528,69 +493,6 @@ export class AWSPinpointProvider implements AnalyticsProvider {
}
}

private async _removeUnusedEndpoints(appId, userId) {
try {
// TODO: re-write with Promise (during refactor pt. 2)
const command: GetUserEndpointsCommand = new GetUserEndpointsCommand({
ApplicationId: appId,
UserId: userId,
});
const data = await this.pinpointClient.send(command);
const endpoints = data.EndpointsResponse.Item;
logger.debug(
`get endpoints associated with the userId: ${userId} with data`,
endpoints
);
let endpointToBeDeleted = endpoints[0];
for (let i = 1; i < endpoints.length; i++) {
const timeStamp1 = Date.parse(endpointToBeDeleted['EffectiveDate']);
const timeStamp2 = Date.parse(endpoints[i]['EffectiveDate']);
// delete the one with invalid effective date
if (isNaN(timeStamp1)) break;
if (isNaN(timeStamp2)) {
endpointToBeDeleted = endpoints[i];
break;
}

if (timeStamp2 < timeStamp1) {
endpointToBeDeleted = endpoints[i];
}
}
// update the endpoint's user id with an empty string
const update_params = {
ApplicationId: appId,
EndpointId: endpointToBeDeleted['Id'],
EndpointRequest: {
User: {
UserId: '',
},
},
};

try {
const updateEndPointcommand: UpdateEndpointCommand = new UpdateEndpointCommand(
update_params
);
const updateEndPointData = await this.pinpointClient.send(
updateEndPointcommand
);
logger.debug(
'The old endpoint is updated with an empty string for user id'
);
return updateEndPointData;
} catch (err) {
logger.debug('Failed to update the endpoint', err);
throw err;
}
} catch (err) {
logger.debug(
`Failed to get endpoints associated with the userId: ${userId} with error`,
err
);
throw err;
}
}

/**
* @private
* @param config
Expand Down Expand Up @@ -621,7 +523,7 @@ export class AWSPinpointProvider implements AnalyticsProvider {
credentials,
customUserAgent: getAmplifyUserAgent(),
});

// TODO: remove this middleware once a long term fix is implemented by aws-sdk-js team.
this.pinpointClient.middlewareStack.addRelativeTo(
next => args => {
Expand Down