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

feat(geo): add updateGeofences api #9312

Merged
merged 3 commits into from
Dec 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
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
136 changes: 136 additions & 0 deletions packages/geo/__tests__/Geo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
mockBatchPutGeofenceCommand,
geofencesWithInvalidId,
mockGetGeofenceCommand,
mockListGeofencesCommand,
} from './data';

LocationClient.prototype.send = jest.fn(async command => {
Expand Down Expand Up @@ -448,6 +449,93 @@ describe('Geo', () => {
});
});

describe('updateGeofences', () => {
test('updateGeofences with a single geofence', async () => {
jest.spyOn(Credentials, 'get').mockImplementationOnce(() => {
return Promise.resolve(credentials);
});

LocationClient.prototype.send = jest
.fn()
.mockImplementationOnce(mockBatchPutGeofenceCommand);

const geo = new GeoClass();
geo.configure(awsConfig);

// Check that results are what's expected
const results = await geo.updateGeofences(validGeofence1);
expect(results).toEqual(singleGeofenceCamelcaseResults);

// Expect that the API was called with the proper input
const spyon = jest.spyOn(LocationClient.prototype, 'send');
const input = spyon.mock.calls[0][0].input;
const output = {
Entries: [
{
GeofenceId: validGeofence1.geofenceId,
Geometry: {
Polygon: validGeofence1.geometry.polygon,
},
},
],
CollectionName: 'geofenceCollectionExample',
};
expect(input).toEqual(output);
});

test('updateGeofences with multiple geofences', async () => {
jest.spyOn(Credentials, 'get').mockImplementationOnce(() => {
return Promise.resolve(credentials);
});

LocationClient.prototype.send = jest
.fn()
.mockImplementation(mockBatchPutGeofenceCommand);

const geo = new GeoClass();
geo.configure(awsConfig);

// Check that results are what's expected
const results = await geo.updateGeofences(validGeofences);
expect(results).toEqual(batchGeofencesCamelcaseResults);

// Expect that the API was called the right amount of times
const expectedNumberOfCalls = Math.floor(validGeofences.length / 10) + 1;
expect(LocationClient.prototype.send).toHaveBeenCalledTimes(
expectedNumberOfCalls
);
});

test('should error if there is a bad geofence in the input', async () => {
jest.spyOn(Credentials, 'get').mockImplementationOnce(() => {
return Promise.resolve(credentials);
});

const geo = new GeoClass();
geo.configure(awsConfig);

await expect(
geo.updateGeofences(geofencesWithInvalidId)
).rejects.toThrowError(
`Invalid geofenceId: t|-|!$ !$ N()T V@|_!D Ids can only contain alphanumeric characters, hyphens, underscores and periods.`
);
});

test('should fail if there is no provider', async () => {
jest.spyOn(Credentials, 'get').mockImplementationOnce(() => {
return Promise.resolve(credentials);
});

const geo = new GeoClass();
geo.configure(awsConfig);
geo.removePluggable('AmazonLocationService');

await expect(geo.updateGeofences(validGeofence1)).rejects.toThrow(
'No plugin found in Geo for the provider'
);
});
});

describe('getGeofence', () => {
test('getGeofence returns the right geofence', async () => {
jest.spyOn(Credentials, 'get').mockImplementationOnce(() => {
Expand Down Expand Up @@ -500,4 +588,52 @@ describe('Geo', () => {
);
});
});

describe('listGeofences', () => {
test('listGeofences gets the first 100 geofences when no arguments are given', async () => {
jest.spyOn(Credentials, 'get').mockImplementationOnce(() => {
return Promise.resolve(credentials);
});

LocationClient.prototype.send = jest
.fn()
.mockImplementationOnce(mockListGeofencesCommand);

const geo = new GeoClass();
geo.configure(awsConfig);

// Check that results are what's expected
const results = await geo.listGeofences();
expect(results.entries.length).toEqual(100);
});

test('listGeofences gets the second 100 geofences when nextToken is passed', async () => {
jest.spyOn(Credentials, 'get').mockImplementation(() => {
return Promise.resolve(credentials);
});

LocationClient.prototype.send = jest
.fn()
.mockImplementation(mockListGeofencesCommand);

const geo = new GeoClass();
geo.configure(awsConfig);

// Check that results are what's expected

const first100Geofences = await geo.listGeofences();

const second100Geofences = await geo.listGeofences({
nextToken: first100Geofences.nextToken,
});

expect(second100Geofences.entries.length).toEqual(100);
expect(second100Geofences.entries[0].geofenceId).toEqual(
'validGeofenceId100'
);
expect(second100Geofences.entries[99].geofenceId).toEqual(
'validGeofenceId199'
);
});
});
});
196 changes: 192 additions & 4 deletions packages/geo/__tests__/Providers/AmazonLocationServiceProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
mockBatchPutGeofenceCommand,
validGeometry,
mockGetGeofenceCommand,
mockListGeofencesCommand,
} from '../data';
import {
SearchByTextOptions,
Expand Down Expand Up @@ -371,7 +372,7 @@ describe('AmazonLocationServiceProvider', () => {

describe('createGeofences', () => {
test('createGeofences with multiple geofences', async () => {
jest.spyOn(Credentials, 'get').mockImplementationOnce(() => {
jest.spyOn(Credentials, 'get').mockImplementation(() => {
return Promise.resolve(credentials);
});

Expand All @@ -388,14 +389,15 @@ describe('AmazonLocationServiceProvider', () => {
});

test('createGeofences calls batchPutGeofences in batches of 10 from input', async () => {
jest.spyOn(Credentials, 'get').mockImplementationOnce(() => {
jest.spyOn(Credentials, 'get').mockImplementation(() => {
return Promise.resolve(credentials);
});

const locationProvider = new AmazonLocationServiceProvider();
locationProvider.configure(awsConfig.geo.amazon_location_service);

const input = createGeofenceInputArray(44);
const numberOfGeofences = 44;
const input = createGeofenceInputArray(numberOfGeofences);

const spyonProvider = jest.spyOn(locationProvider, 'createGeofences');
const spyonClient = jest.spyOn(LocationClient.prototype, 'send');
Expand Down Expand Up @@ -424,7 +426,7 @@ describe('AmazonLocationServiceProvider', () => {
});

test('createGeofences properly handles errors with bad network calls', async () => {
jest.spyOn(Credentials, 'get').mockImplementationOnce(() => {
jest.spyOn(Credentials, 'get').mockImplementation(() => {
return Promise.resolve(credentials);
});

Expand Down Expand Up @@ -495,6 +497,133 @@ describe('AmazonLocationServiceProvider', () => {
});
});

describe('updateGeofences', () => {
test('updateGeofences with multiple geofences', async () => {
jest.spyOn(Credentials, 'get').mockImplementation(() => {
return Promise.resolve(credentials);
});

LocationClient.prototype.send = jest
.fn()
.mockImplementation(mockBatchPutGeofenceCommand);

const locationProvider = new AmazonLocationServiceProvider();
locationProvider.configure(awsConfig.geo.amazon_location_service);

const results = await locationProvider.updateGeofences(validGeofences);

expect(results).toEqual(batchGeofencesCamelcaseResults);
});

test('updateGeofences calls batchPutGeofences in batches of 10 from input', async () => {
jest.spyOn(Credentials, 'get').mockImplementation(() => {
return Promise.resolve(credentials);
});

const locationProvider = new AmazonLocationServiceProvider();
locationProvider.configure(awsConfig.geo.amazon_location_service);

const numberOfGeofences = 44;
const input = createGeofenceInputArray(numberOfGeofences);

const spyonProvider = jest.spyOn(locationProvider, 'updateGeofences');
const spyonClient = jest.spyOn(LocationClient.prototype, 'send');

const results = await locationProvider.updateGeofences(input);

const expected = {
successes: input.map(({ geofenceId }) => {
return {
geofenceId,
createTime: '2020-04-01T21:00:00.000Z',
updateTime: '2020-04-01T21:00:00.000Z',
};
}),
errors: [],
};
expect(results).toEqual(expected);

const spyProviderInput = spyonProvider.mock.calls[0][0];

const spyClientInput = spyonClient.mock.calls;

expect(spyClientInput.length).toEqual(
Math.ceil(spyProviderInput.length / 10)
);
});

test('updateGeofences properly handles errors with bad network calls', async () => {
jest.spyOn(Credentials, 'get').mockImplementation(() => {
return Promise.resolve(credentials);
});

const locationProvider = new AmazonLocationServiceProvider();
locationProvider.configure(awsConfig.geo.amazon_location_service);

const input = createGeofenceInputArray(44);
input[22].geofenceId = 'badId';
const validEntries = [...input.slice(0, 20), ...input.slice(30, 44)];

const spyonClient = jest.spyOn(LocationClient.prototype, 'send');
spyonClient.mockImplementation(geofenceInput => {
const entries = geofenceInput.input as any;

if (entries.Entries.some(entry => entry.GeofenceId === 'badId')) {
return Promise.reject(new Error('Bad network call'));
}

const resolution = {
Successes: entries.Entries.map(({ GeofenceId }) => {
return {
GeofenceId,
CreateTime: '2020-04-01T21:00:00.000Z',
UpdateTime: '2020-04-01T21:00:00.000Z',
};
}),
Errors: [],
};
return Promise.resolve(resolution);
});

const results = await locationProvider.updateGeofences(input);
const badResults = input.slice(20, 30).map(input => {
return {
error: {
code: 'APIConnectionError',
message: 'Bad network call',
},
geofenceId: input.geofenceId,
};
});
const expected = {
successes: validEntries.map(({ geofenceId }) => {
return {
geofenceId,
createTime: '2020-04-01T21:00:00.000Z',
updateTime: '2020-04-01T21:00:00.000Z',
};
}),
errors: badResults,
};
expect(results).toEqual(expected);
});

test('should error if there are no geofenceCollections in config', async () => {
jest.spyOn(Credentials, 'get').mockImplementationOnce(() => {
return Promise.resolve(credentials);
});

const locationProvider = new AmazonLocationServiceProvider();
locationProvider.configure({});

await expect(
locationProvider.updateGeofences(validGeofences)
).rejects.toThrow(
'No Geofence Collections found, please run `amplify add geo` to create one and run `amplify push` after.'
);
});
});

describe('getGeofence', () => {
test('getGeofence returns the right geofence', async () => {
jest.spyOn(Credentials, 'get').mockImplementationOnce(() => {
Expand Down Expand Up @@ -535,4 +664,63 @@ describe('AmazonLocationServiceProvider', () => {
);
});
});

describe('listGeofences', () => {
test('listGeofences gets the first 100 geofences when no arguments are given', async () => {
jest.spyOn(Credentials, 'get').mockImplementationOnce(() => {
return Promise.resolve(credentials);
});

LocationClient.prototype.send = jest
.fn()
.mockImplementation(mockListGeofencesCommand);

const locationProvider = new AmazonLocationServiceProvider();
locationProvider.configure(awsConfig.geo.amazon_location_service);

const geofences = await locationProvider.listGeofences();

expect(geofences.entries.length).toEqual(100);
});

test('listGeofences gets the second 100 geofences when nextToken is passed', async () => {
jest.spyOn(Credentials, 'get').mockImplementation(() => {
return Promise.resolve(credentials);
});

LocationClient.prototype.send = jest
.fn()
.mockImplementation(mockListGeofencesCommand);

const locationProvider = new AmazonLocationServiceProvider();
locationProvider.configure(awsConfig.geo.amazon_location_service);

const first100Geofences = await locationProvider.listGeofences();

const second100Geofences = await locationProvider.listGeofences({
nextToken: first100Geofences.nextToken,
});

expect(second100Geofences.entries.length).toEqual(100);
expect(second100Geofences.entries[0].geofenceId).toEqual(
'validGeofenceId100'
);
expect(second100Geofences.entries[99].geofenceId).toEqual(
'validGeofenceId199'
);
});

test('should error if there are no geofenceCollections in config', async () => {
jest.spyOn(Credentials, 'get').mockImplementationOnce(() => {
return Promise.resolve(credentials);
});

const locationProvider = new AmazonLocationServiceProvider();
locationProvider.configure({});

await expect(locationProvider.listGeofences()).rejects.toThrow(
'No Geofence Collections found, please run `amplify add geo` to create one and run `amplify push` after.'
);
});
});
});
Loading