Skip to content

Commit

Permalink
feat(geoprocessing): api-events wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
kgajowy committed May 17, 2021
1 parent 8e2cfc9 commit 877dfe3
Show file tree
Hide file tree
Showing 11 changed files with 147 additions and 6 deletions.
3 changes: 3 additions & 0 deletions api/src/modules/api-events/api-event.api.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ export const apiEventResource: BaseServiceResource = {
/**
* Available kinds of API Events. See the Event.kind prop documentation below
* for more information.
*
* If ever changing this, adjust
* @file geoprocessing/src/modules/api-events/events.enum.ts
*/
export enum API_EVENT_KINDS {
user__signedUp__v1alpha1 = 'user.signedUp/v1alpha1',
Expand Down
1 change: 1 addition & 0 deletions env.default
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ API_LOGGING_MUTE_ALL=true
# `dd if=/dev/urandom bs=64 count=1 2>/dev/null | base64 -w0` is a simple way
# to generate a strong random secret.
API_AUTH_X_API_KEY=
API_SERVICE_URL=http://localhost:3030
11 changes: 7 additions & 4 deletions geoprocessing/config/custom-environment-variables.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,22 @@
"postgresApi": {
"url": "API_POSTGRES_URL",
"runMigrationsOnStartup": "API_RUN_MIGRATIONS_ON_STARTUP"
},
"postgresGeoApi": {
},
"postgresGeoApi": {
"url": "GEO_POSTGRES_URL",
"runMigrationsOnStartup": "GEOPROCESSING_RUN_MIGRATIONS_ON_STARTUP"
},
},
"network": {
"cors": {
"origins_extra": "NETWORK_CORS_ORIGINS"
"origins_extra": "NETWORK_CORS_ORIGINS"
}
},
"auth": {
"xApiKey": {
"secret": "API_AUTH_X_API_KEY"
}
},
"api": {
"url": "API_SERVICE_URL"
}
}
5 changes: 4 additions & 1 deletion geoprocessing/config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@
"host": "marxan-redis",
"port": "6379"
}
}
},
"api": {
"url": "http://localhost:3000"
}
}
1 change: 1 addition & 0 deletions geoprocessing/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
"@types/unzipper": "^0.10.3",
"@typescript-eslint/eslint-plugin": "^4.6.1",
"@typescript-eslint/parser": "^4.6.1",
"axios-mock-adapter": "1.19.0",
"bunyan": "^1.8.14",
"eslint": "^7.12.1",
"eslint-config-prettier": "^6.15.0",
Expand Down
2 changes: 2 additions & 0 deletions geoprocessing/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ProtectedAreasModule } from 'src/modules/protected-areas/protected-area
import { PlanningUnitsModule } from 'src/modules/planning-units/planning-units.module';
import { TileModule } from './modules/tile/tile.module';
import { FeaturesModule } from 'src/modules/features/features.module';
import { ApiEventsModule } from './modules/api-events/api-events.module';

@Module({
imports: [
Expand All @@ -24,6 +25,7 @@ import { FeaturesModule } from 'src/modules/features/features.module';
TileModule,
ProtectedAreasModule,
FeaturesModule,
ApiEventsModule,
],
controllers: [AppController],
providers: [AppService],
Expand Down
9 changes: 9 additions & 0 deletions geoprocessing/src/modules/api-events/api-events.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { HttpModule, Module } from '@nestjs/common';
import { ApiEventsService } from './api-events.service';

@Module({
imports: [HttpModule],
providers: [ApiEventsService],
exports: [ApiEventsService],
})
export class ApiEventsModule {}
42 changes: 42 additions & 0 deletions geoprocessing/src/modules/api-events/api-events.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { HttpService, Injectable } from '@nestjs/common';
import { API_EVENT_KINDS } from './events.enum';
import { AppConfig } from '../../utils/config.utils';

@Injectable()
export class ApiEventsService {
readonly #secret: string;
readonly #apiUrl: string;

constructor(private readonly http: HttpService) {
// TODO debt: config shall be injected (nestjs/config); it isn't really unit-testable
// will throw if not provided
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.#secret = AppConfig.get<string>('auth.xApiKey.secret')!;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.#apiUrl = AppConfig.get<string>('api.url')!;
}

async create<T>(
resourceId: string,
kind: API_EVENT_KINDS,
data?: T,
): Promise<void> {
// TODO what if it failed? (currently validateStatus "swallows" the error)
await this.http
.post(
this.#apiUrl + `/v1/api-events`,
{
kind,
topic: resourceId,
data,
},
{
headers: {
Accept: 'application/json',
},
validateStatus: () => true,
},
)
.toPromise();
}
}
13 changes: 13 additions & 0 deletions geoprocessing/src/modules/api-events/events.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* duplicated
* @file api/src/modules/api-events/api-event.topic+kind.api.entity.ts
*/
export enum API_EVENT_KINDS {
user__signedUp__v1alpha1 = 'user.signedUp/v1alpha1',
user__accountActivationTokenGenerated__v1alpha1 = 'user.accountActivationTokenGenerated/v1alpha1',
user__accountActivationSucceeded__v1alpha1 = 'user.accountActivationSucceeded/v1alpha1',
user__accountActivationFailed__v1alpha1 = 'user.accountActivationFailed/v1alpha1',
user__passwordResetTokenGenerated__v1alpha1 = 'user.passwordResetTokenGenerated/v1alpha1',
user__passwordResetSucceeded__v1alpha1 = 'user.passwordResetSucceeded/v1alpha1',
user__passwordResetFailed__v1alpha1 = 'user.passwordResetFailed/v1alpha1',
}
51 changes: 51 additions & 0 deletions geoprocessing/test/api-events.service.integration.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Test } from '@nestjs/testing';
import AxiosMockAdapter from 'axios-mock-adapter';
import Axios from 'axios';
import { HttpService } from '@nestjs/common';
import { ApiEventsService } from '../src/modules/api-events/api-events.service';
import { API_EVENT_KINDS } from '../src/modules/api-events/events.enum';

let sut: ApiEventsService;
let axiosMock: AxiosMockAdapter;

const axios = Axios.create();

beforeEach(async () => {
axiosMock = new AxiosMockAdapter(axios, {
onNoMatch: 'throwException',
});
const sandbox = await Test.createTestingModule({
providers: [
{
provide: `AXIOS_INSTANCE_TOKEN`,
useValue: axios,
},
HttpService,
ApiEventsService,
],
}).compile();
sut = await sandbox.get(ApiEventsService);
});

describe(`when creating an event succeed`, () => {
const resourceId = `resource-id`;
const data = {
payload: 'value',
} as const;
const kind = API_EVENT_KINDS.user__accountActivationTokenGenerated__v1alpha1;

beforeEach(() => {
axiosMock
.onPost('http://localhost:3030/v1/api-events', {
kind: 'user.accountActivationTokenGenerated/v1alpha1',
topic: resourceId,
data,
})
.replyOnce(201, {});
});

it(`should submit the required data`, async () => {
await sut.create(resourceId, kind, data);
expect(axiosMock.history.post.length).toEqual(1);
});
});
15 changes: 14 additions & 1 deletion geoprocessing/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2704,6 +2704,14 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==

axios-mock-adapter@1.19.0:
version "1.19.0"
resolved "https://registry.yarnpkg.com/axios-mock-adapter/-/axios-mock-adapter-1.19.0.tgz#9d72e321a6c5418e1eff067aa99761a86c5188a4"
integrity sha512-D+0U4LNPr7WroiBDvWilzTMYPYTuZlbo6BI8YHZtj7wYQS8NkARlP9KBt8IWWHTQJ0q/8oZ0ClPBtKCCkx8cQg==
dependencies:
fast-deep-equal "^3.1.3"
is-buffer "^2.0.3"

axios@0.21.1:
version "0.21.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
Expand Down Expand Up @@ -4263,7 +4271,7 @@ faker@^5.5.2:
resolved "https://registry.yarnpkg.com/faker/-/faker-5.5.3.tgz#c57974ee484431b25205c2c8dc09fda861e51e0e"
integrity sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g==

fast-deep-equal@^3.1.1:
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
Expand Down Expand Up @@ -5101,6 +5109,11 @@ is-buffer@^1.1.5:
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==

is-buffer@^2.0.3:
version "2.0.5"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191"
integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==

is-ci@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c"
Expand Down

0 comments on commit 877dfe3

Please sign in to comment.