@loopback/example-greeting-app 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierignore +2 -0
- package/.prettierrc +7 -0
- package/.vscode/settings.json +19 -0
- package/.vscode/tasks.json +29 -0
- package/CHANGELOG.md +552 -0
- package/LICENSE +25 -0
- package/README.md +61 -0
- package/dist/__tests__/integration/greeting-service.integration.d.ts +1 -0
- package/dist/__tests__/integration/greeting-service.integration.js +68 -0
- package/dist/__tests__/integration/greeting-service.integration.js.map +1 -0
- package/dist/application.d.ts +98 -0
- package/dist/application.js +29 -0
- package/dist/application.js.map +1 -0
- package/dist/caching-service.d.ts +64 -0
- package/dist/caching-service.js +133 -0
- package/dist/caching-service.js.map +1 -0
- package/dist/controllers/greeting.controller.d.ts +10 -0
- package/dist/controllers/greeting.controller.js +59 -0
- package/dist/controllers/greeting.controller.js.map +1 -0
- package/dist/controllers/index.d.ts +1 -0
- package/dist/controllers/index.js +9 -0
- package/dist/controllers/index.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/interceptors/caching.interceptor.d.ts +7 -0
- package/dist/interceptors/caching.interceptor.js +49 -0
- package/dist/interceptors/caching.interceptor.js.map +1 -0
- package/dist/interceptors/index.d.ts +1 -0
- package/dist/interceptors/index.js +9 -0
- package/dist/interceptors/index.js.map +1 -0
- package/dist/keys.d.ts +6 -0
- package/dist/keys.js +13 -0
- package/dist/keys.js.map +1 -0
- package/dist/observers/cache.observer.d.ts +19 -0
- package/dist/observers/cache.observer.js +39 -0
- package/dist/observers/cache.observer.js.map +1 -0
- package/dist/observers/index.d.ts +1 -0
- package/dist/observers/index.js +9 -0
- package/dist/observers/index.js.map +1 -0
- package/dist/openapi-spec.d.ts +1 -0
- package/dist/openapi-spec.js +28 -0
- package/dist/openapi-spec.js.map +1 -0
- package/dist/types.d.ts +8 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/greeting-app.png +0 -0
- package/package.json +69 -0
- package/src/__tests__/integration/greeting-service.integration.ts +79 -0
- package/src/application.ts +27 -0
- package/src/caching-service.ts +146 -0
- package/src/controllers/greeting.controller.ts +50 -0
- package/src/controllers/index.ts +6 -0
- package/src/index.ts +33 -0
- package/src/interceptors/caching.interceptor.ts +55 -0
- package/src/interceptors/index.ts +6 -0
- package/src/keys.ts +14 -0
- package/src/observers/cache.observer.ts +34 -0
- package/src/observers/index.ts +6 -0
- package/src/openapi-spec.ts +28 -0
- package/src/types.ts +13 -0
- package/tsconfig.json +30 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Copyright (c) IBM Corp. 2019.
|
|
2
|
+
Node module: @loopback/example-greeting-app
|
|
3
|
+
This project is licensed under the MIT License, full text below.
|
|
4
|
+
|
|
5
|
+
--------
|
|
6
|
+
|
|
7
|
+
MIT license
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
in the Software without restriction, including without limitation the rights
|
|
12
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in
|
|
17
|
+
all copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
25
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# @loopback/example-greeting-app
|
|
2
|
+
|
|
3
|
+
This application is built on top of
|
|
4
|
+
[`@loopback/example-greeter-extension`](https://github.com/loopbackio/loopback-next/tree/master/examples/greeter-extension).
|
|
5
|
+
|
|
6
|
+
## Compose the application
|
|
7
|
+
|
|
8
|
+
1. Add REST API
|
|
9
|
+
|
|
10
|
+
- GreetingController - providing endpoints over `GreetingService`
|
|
11
|
+
|
|
12
|
+
2. Add caching
|
|
13
|
+
|
|
14
|
+
- CachingService - implementing cache operations, such as set,get, delete,
|
|
15
|
+
and sweep
|
|
16
|
+
- CachingInterceptor - intercepting http requests to apply caching
|
|
17
|
+
- CacheObserver - watching and sweeping cache in the background
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+
|
|
21
|
+
## Contributions
|
|
22
|
+
|
|
23
|
+
- [Guidelines](https://github.com/loopbackio/loopback-next/blob/master/docs/CONTRIBUTING.md)
|
|
24
|
+
- [Join the team](https://github.com/loopbackio/loopback-next/issues/110)
|
|
25
|
+
|
|
26
|
+
## Try out
|
|
27
|
+
|
|
28
|
+
Run `npm start`:
|
|
29
|
+
|
|
30
|
+
The service is running at http://127.0.0.1:3000/greet/world.
|
|
31
|
+
|
|
32
|
+
Open your browser to try the REST APIs. You can replace `world` with other
|
|
33
|
+
names.
|
|
34
|
+
|
|
35
|
+
The following `curl` command sets `Accept-Language` header to `zh` to receive
|
|
36
|
+
the greeting in Chinese.
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
curl -H 'Accept-Language: zh' http://127.0.0.1:3000/greet/Ray
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"timestamp": "2019-05-29T22:48:03.040Z",
|
|
45
|
+
"language": "zh",
|
|
46
|
+
"greeting": "Ray,你好!"
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Tests
|
|
51
|
+
|
|
52
|
+
Run `npm test` from the root folder.
|
|
53
|
+
|
|
54
|
+
## Contributors
|
|
55
|
+
|
|
56
|
+
See
|
|
57
|
+
[all contributors](https://github.com/loopbackio/loopback-next/graphs/contributors).
|
|
58
|
+
|
|
59
|
+
## License
|
|
60
|
+
|
|
61
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright IBM Corp. 2019,2020. All Rights Reserved.
|
|
3
|
+
// Node module: @loopback/example-greeting-app
|
|
4
|
+
// This file is licensed under the MIT License.
|
|
5
|
+
// License text available at https://opensource.org/licenses/MIT
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const testlab_1 = require("@loopback/testlab");
|
|
8
|
+
const util_1 = require("util");
|
|
9
|
+
const __1 = require("../..");
|
|
10
|
+
const keys_1 = require("../../keys");
|
|
11
|
+
describe('GreetingApplication', () => {
|
|
12
|
+
let app;
|
|
13
|
+
let client;
|
|
14
|
+
before(givenRunningApplicationWithCustomConfiguration);
|
|
15
|
+
after(() => app.stop());
|
|
16
|
+
before(() => {
|
|
17
|
+
client = testlab_1.createRestAppClient(app);
|
|
18
|
+
});
|
|
19
|
+
it('gets a greeting in English', async function () {
|
|
20
|
+
const response = await client
|
|
21
|
+
.get('/greet/Raymond')
|
|
22
|
+
.set('Accept-Language', 'en')
|
|
23
|
+
.expect(200);
|
|
24
|
+
testlab_1.expect(response.body).to.containEql({
|
|
25
|
+
language: 'en',
|
|
26
|
+
greeting: 'Hello, Raymond!',
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
it('gets a greeting in Chinese', async function () {
|
|
30
|
+
const response = await client
|
|
31
|
+
.get('/greet/Raymond')
|
|
32
|
+
.set('Accept-Language', 'zh')
|
|
33
|
+
.expect(200);
|
|
34
|
+
testlab_1.expect(response.body).to.containEql({
|
|
35
|
+
language: 'zh',
|
|
36
|
+
greeting: 'Raymond,你好!',
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
it('gets a greeting from cache', async function () {
|
|
40
|
+
app.configure(keys_1.CACHING_SERVICE).to({ ttl: 100 });
|
|
41
|
+
let response = await client
|
|
42
|
+
.get('/greet/Raymond')
|
|
43
|
+
.set('Accept-Language', 'en')
|
|
44
|
+
.expect(200);
|
|
45
|
+
const msg1 = response.body;
|
|
46
|
+
// Now the result should be cached
|
|
47
|
+
response = await client
|
|
48
|
+
.get('/greet/Raymond')
|
|
49
|
+
.set('Accept-Language', 'en')
|
|
50
|
+
.expect(200);
|
|
51
|
+
testlab_1.expect(response.body).to.eql(msg1);
|
|
52
|
+
// Cache should be expired now
|
|
53
|
+
await util_1.promisify(setTimeout)(200);
|
|
54
|
+
response = await client
|
|
55
|
+
.get('/greet/Raymond')
|
|
56
|
+
.set('Accept-Language', 'en')
|
|
57
|
+
.expect(200);
|
|
58
|
+
testlab_1.expect(response.body.timestamp).to.not.eql(msg1.timestamp);
|
|
59
|
+
});
|
|
60
|
+
async function givenRunningApplicationWithCustomConfiguration() {
|
|
61
|
+
app = new __1.GreetingApplication({
|
|
62
|
+
rest: testlab_1.givenHttpServerConfig(),
|
|
63
|
+
});
|
|
64
|
+
// Start Application
|
|
65
|
+
await app.main();
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
//# sourceMappingURL=greeting-service.integration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"greeting-service.integration.js","sourceRoot":"","sources":["../../../src/__tests__/integration/greeting-service.integration.ts"],"names":[],"mappings":";AAAA,sDAAsD;AACtD,8CAA8C;AAC9C,+CAA+C;AAC/C,gEAAgE;;AAEhE,+CAK2B;AAC3B,+BAA+B;AAC/B,6BAA0C;AAC1C,qCAA2C;AAE3C,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,IAAI,GAAwB,CAAC;IAC7B,IAAI,MAAc,CAAC;IAEnB,MAAM,CAAC,8CAA8C,CAAC,CAAC;IACvD,KAAK,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IAExB,MAAM,CAAC,GAAG,EAAE;QACV,MAAM,GAAG,6BAAmB,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,KAAK;QACpC,MAAM,QAAQ,GAAG,MAAM,MAAM;aAC1B,GAAG,CAAC,gBAAgB,CAAC;aACrB,GAAG,CAAC,iBAAiB,EAAE,IAAI,CAAC;aAC5B,MAAM,CAAC,GAAG,CAAC,CAAC;QACf,gBAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC;YAClC,QAAQ,EAAE,IAAI;YACd,QAAQ,EAAE,iBAAiB;SAC5B,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,KAAK;QACpC,MAAM,QAAQ,GAAG,MAAM,MAAM;aAC1B,GAAG,CAAC,gBAAgB,CAAC;aACrB,GAAG,CAAC,iBAAiB,EAAE,IAAI,CAAC;aAC5B,MAAM,CAAC,GAAG,CAAC,CAAC;QACf,gBAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC;YAClC,QAAQ,EAAE,IAAI;YACd,QAAQ,EAAE,aAAa;SACxB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,KAAK;QACpC,GAAG,CAAC,SAAS,CAAC,sBAAe,CAAC,CAAC,EAAE,CAAC,EAAC,GAAG,EAAE,GAAG,EAAC,CAAC,CAAC;QAC9C,IAAI,QAAQ,GAAG,MAAM,MAAM;aACxB,GAAG,CAAC,gBAAgB,CAAC;aACrB,GAAG,CAAC,iBAAiB,EAAE,IAAI,CAAC;aAC5B,MAAM,CAAC,GAAG,CAAC,CAAC;QACf,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC;QAC3B,kCAAkC;QAClC,QAAQ,GAAG,MAAM,MAAM;aACpB,GAAG,CAAC,gBAAgB,CAAC;aACrB,GAAG,CAAC,iBAAiB,EAAE,IAAI,CAAC;aAC5B,MAAM,CAAC,GAAG,CAAC,CAAC;QACf,gBAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACnC,8BAA8B;QAC9B,MAAM,gBAAS,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC;QACjC,QAAQ,GAAG,MAAM,MAAM;aACpB,GAAG,CAAC,gBAAgB,CAAC;aACrB,GAAG,CAAC,iBAAiB,EAAE,IAAI,CAAC;aAC5B,MAAM,CAAC,GAAG,CAAC,CAAC;QACf,gBAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,KAAK,UAAU,8CAA8C;QAC3D,GAAG,GAAG,IAAI,uBAAmB,CAAC;YAC5B,IAAI,EAAE,+BAAqB,EAAE;SAC9B,CAAC,CAAC;QAEH,oBAAoB;QACpB,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IACnB,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { ApplicationConfig } from '@loopback/core';
|
|
2
|
+
import { RestApplication } from '@loopback/rest';
|
|
3
|
+
declare const GreetingApplication_base: (new (...args: any[]) => {
|
|
4
|
+
projectRoot: string;
|
|
5
|
+
bootOptions?: import("@loopback/boot").BootOptions | undefined;
|
|
6
|
+
booted: boolean;
|
|
7
|
+
start(): Promise<void>;
|
|
8
|
+
boot(): Promise<void>;
|
|
9
|
+
booters(...booterCls: import("@loopback/core").Constructor<import("@loopback/boot").Booter>[]): import("@loopback/boot").Binding<any>[];
|
|
10
|
+
applicationBooter(subApp: import("@loopback/core").Application & import("@loopback/boot").Bootable, filter?: import("@loopback/core").BindingFilter | undefined): import("@loopback/boot").Binding<import("@loopback/boot").Booter>;
|
|
11
|
+
component<C extends import("@loopback/core").Component = import("@loopback/core").Component>(componentCtor: import("@loopback/core").Constructor<C>, nameOrOptions?: string | import("@loopback/core").BindingFromClassOptions | undefined): import("@loopback/boot").Binding<C>;
|
|
12
|
+
mountComponentBooters(componentInstanceOrClass: import("@loopback/core").Constructor<unknown> | import("@loopback/boot").InstanceWithBooters): void;
|
|
13
|
+
readonly options: ApplicationConfig;
|
|
14
|
+
readonly state: string;
|
|
15
|
+
controller: <T_1>(controllerCtor: import("@loopback/core").ControllerClass<T_1>, nameOrOptions?: string | import("@loopback/core").BindingFromClassOptions | undefined) => import("@loopback/boot").Binding<T_1>;
|
|
16
|
+
server: <T_2 extends import("@loopback/core").Server>(ctor: import("@loopback/core").Constructor<T_2>, nameOrOptions?: string | import("@loopback/core").BindingFromClassOptions | undefined) => import("@loopback/boot").Binding<T_2>;
|
|
17
|
+
servers: <T_3 extends import("@loopback/core").Server>(ctors: import("@loopback/core").Constructor<T_3>[]) => import("@loopback/boot").Binding<any>[];
|
|
18
|
+
getServer: <T_4 extends import("@loopback/core").Server>(target: string | import("@loopback/core").Constructor<T_4>) => Promise<T_4>;
|
|
19
|
+
init: () => Promise<void>;
|
|
20
|
+
onInit: (fn: () => import("@loopback/core").ValueOrPromise<void>) => import("@loopback/boot").Binding<import("@loopback/core").LifeCycleObserver>;
|
|
21
|
+
onStart: (fn: () => import("@loopback/core").ValueOrPromise<void>) => import("@loopback/boot").Binding<import("@loopback/core").LifeCycleObserver>;
|
|
22
|
+
stop: () => Promise<void>;
|
|
23
|
+
onStop: (fn: () => import("@loopback/core").ValueOrPromise<void>) => import("@loopback/boot").Binding<import("@loopback/core").LifeCycleObserver>;
|
|
24
|
+
setMetadata: (metadata: import("@loopback/core").ApplicationMetadata) => void;
|
|
25
|
+
lifeCycleObserver: <T_5 extends import("@loopback/core").LifeCycleObserver>(ctor: import("@loopback/core").Constructor<T_5>, nameOrOptions?: string | import("@loopback/core").BindingFromClassOptions | undefined) => import("@loopback/boot").Binding<T_5>;
|
|
26
|
+
service: <S>(cls: import("@loopback/core").ServiceOrProviderClass<S>, nameOrOptions?: string | import("@loopback/core").ServiceOptions | undefined) => import("@loopback/boot").Binding<S>;
|
|
27
|
+
interceptor: (interceptor: import("@loopback/core").Interceptor | import("@loopback/core").Constructor<import("@loopback/core").Provider<import("@loopback/core").Interceptor>>, nameOrOptions?: string | import("@loopback/core").InterceptorBindingOptions | undefined) => import("@loopback/boot").Binding<import("@loopback/core").Interceptor>;
|
|
28
|
+
readonly name: string;
|
|
29
|
+
readonly subscriptionManager: import("@loopback/core").ContextSubscriptionManager;
|
|
30
|
+
scope: import("@loopback/core").BindingScope;
|
|
31
|
+
readonly parent: import("@loopback/core").Context | undefined;
|
|
32
|
+
emitEvent: <T_6 extends import("@loopback/core").ContextEvent>(type: string, event: T_6) => void;
|
|
33
|
+
emitError: (err: unknown) => void;
|
|
34
|
+
bind: <ValueType = any>(key: import("@loopback/core").BindingAddress<ValueType>) => import("@loopback/boot").Binding<ValueType>;
|
|
35
|
+
add: (binding: import("@loopback/boot").Binding<unknown>) => import("@loopback/core").Application;
|
|
36
|
+
configure: <ConfigValueType = any>(key?: import("@loopback/core").BindingAddress<unknown> | undefined) => import("@loopback/boot").Binding<ConfigValueType>;
|
|
37
|
+
getConfigAsValueOrPromise: <ConfigValueType_1>(key: import("@loopback/core").BindingAddress<unknown>, propertyPath?: string | undefined, resolutionOptions?: import("@loopback/core").ResolutionOptions | undefined) => import("@loopback/core").ValueOrPromise<ConfigValueType_1 | undefined>;
|
|
38
|
+
getConfig: <ConfigValueType_2>(key: import("@loopback/core").BindingAddress<unknown>, propertyPath?: string | undefined, resolutionOptions?: import("@loopback/core").ResolutionOptions | undefined) => Promise<ConfigValueType_2 | undefined>;
|
|
39
|
+
getConfigSync: <ConfigValueType_3>(key: import("@loopback/core").BindingAddress<unknown>, propertyPath?: string | undefined, resolutionOptions?: import("@loopback/core").ResolutionOptions | undefined) => ConfigValueType_3 | undefined;
|
|
40
|
+
unbind: (key: import("@loopback/core").BindingAddress<unknown>) => boolean;
|
|
41
|
+
subscribe: (observer: import("@loopback/core").ContextEventObserver) => import("@loopback/core").Subscription;
|
|
42
|
+
unsubscribe: (observer: import("@loopback/core").ContextEventObserver) => boolean;
|
|
43
|
+
close: () => void;
|
|
44
|
+
isSubscribed: (observer: import("@loopback/core").ContextObserver) => boolean;
|
|
45
|
+
createView: <T_7 = unknown>(filter: import("@loopback/core").BindingFilter, comparator?: import("@loopback/core").BindingComparator | undefined) => import("@loopback/core").ContextView<T_7>;
|
|
46
|
+
contains: (key: import("@loopback/core").BindingAddress<unknown>) => boolean;
|
|
47
|
+
isBound: (key: import("@loopback/core").BindingAddress<unknown>) => boolean;
|
|
48
|
+
getOwnerContext: (keyOrBinding: import("@loopback/core").BindingAddress<unknown> | Readonly<import("@loopback/boot").Binding<unknown>>) => import("@loopback/core").Context | undefined;
|
|
49
|
+
getScopedContext: (scope: import("@loopback/core").BindingScope.APPLICATION | import("@loopback/core").BindingScope.SERVER | import("@loopback/core").BindingScope.REQUEST) => import("@loopback/core").Context | undefined;
|
|
50
|
+
getResolutionContext: (binding: Readonly<import("@loopback/boot").Binding<unknown>>) => import("@loopback/core").Context | undefined;
|
|
51
|
+
isVisibleTo: (ctx: import("@loopback/core").Context) => boolean;
|
|
52
|
+
find: <ValueType_1 = any>(pattern?: string | RegExp | import("@loopback/core").BindingFilter | undefined) => Readonly<import("@loopback/boot").Binding<ValueType_1>>[];
|
|
53
|
+
findByTag: <ValueType_2 = any>(tagFilter: RegExp | import("@loopback/core").BindingTag) => Readonly<import("@loopback/boot").Binding<ValueType_2>>[];
|
|
54
|
+
get: {
|
|
55
|
+
<ValueType_3>(keyWithPath: import("@loopback/core").BindingAddress<ValueType_3>, session?: import("@loopback/core").ResolutionSession | undefined): Promise<ValueType_3>;
|
|
56
|
+
<ValueType_4>(keyWithPath: import("@loopback/core").BindingAddress<ValueType_4>, options: import("@loopback/core").ResolutionOptions): Promise<ValueType_4 | undefined>;
|
|
57
|
+
};
|
|
58
|
+
getSync: {
|
|
59
|
+
<ValueType_5>(keyWithPath: import("@loopback/core").BindingAddress<ValueType_5>, session?: import("@loopback/core").ResolutionSession | undefined): ValueType_5;
|
|
60
|
+
<ValueType_6>(keyWithPath: import("@loopback/core").BindingAddress<ValueType_6>, options?: import("@loopback/core").ResolutionOptions | undefined): ValueType_6 | undefined;
|
|
61
|
+
};
|
|
62
|
+
getBinding: {
|
|
63
|
+
<ValueType_7 = any>(key: import("@loopback/core").BindingAddress<ValueType_7>): import("@loopback/boot").Binding<ValueType_7>;
|
|
64
|
+
<ValueType_8>(key: import("@loopback/core").BindingAddress<ValueType_8>, options?: {
|
|
65
|
+
optional?: boolean | undefined;
|
|
66
|
+
} | undefined): import("@loopback/boot").Binding<ValueType_8> | undefined;
|
|
67
|
+
};
|
|
68
|
+
findOrCreateBinding: <T_8>(key: import("@loopback/core").BindingAddress<T_8>, policy?: import("@loopback/core").BindingCreationPolicy | undefined) => import("@loopback/boot").Binding<T_8>;
|
|
69
|
+
getValueOrPromise: <ValueType_9>(keyWithPath: import("@loopback/core").BindingAddress<ValueType_9>, optionsOrSession?: import("@loopback/core").ResolutionOptionsOrSession | undefined) => import("@loopback/core").ValueOrPromise<ValueType_9 | undefined>;
|
|
70
|
+
toJSON: () => import("@loopback/core").JSONObject;
|
|
71
|
+
inspect: (options?: import("@loopback/core").ContextInspectOptions | undefined) => import("@loopback/core").JSONObject;
|
|
72
|
+
on: {
|
|
73
|
+
(eventName: "bind" | "unbind", listener: import("@loopback/core").ContextEventListener): import("@loopback/core").Application;
|
|
74
|
+
(event: string | symbol, listener: (...args: any[]) => void): import("@loopback/core").Application;
|
|
75
|
+
};
|
|
76
|
+
once: {
|
|
77
|
+
(eventName: "bind" | "unbind", listener: import("@loopback/core").ContextEventListener): import("@loopback/core").Application;
|
|
78
|
+
(event: string | symbol, listener: (...args: any[]) => void): import("@loopback/core").Application;
|
|
79
|
+
};
|
|
80
|
+
addListener: (event: string | symbol, listener: (...args: any[]) => void) => import("@loopback/core").Application;
|
|
81
|
+
removeListener: (event: string | symbol, listener: (...args: any[]) => void) => import("@loopback/core").Application;
|
|
82
|
+
off: (event: string | symbol, listener: (...args: any[]) => void) => import("@loopback/core").Application;
|
|
83
|
+
removeAllListeners: (event?: string | symbol | undefined) => import("@loopback/core").Application;
|
|
84
|
+
setMaxListeners: (n: number) => import("@loopback/core").Application;
|
|
85
|
+
getMaxListeners: () => number;
|
|
86
|
+
listeners: (event: string | symbol) => Function[];
|
|
87
|
+
rawListeners: (event: string | symbol) => Function[];
|
|
88
|
+
emit: (event: string | symbol, ...args: any[]) => boolean;
|
|
89
|
+
listenerCount: (type: string | symbol) => number;
|
|
90
|
+
prependListener: (event: string | symbol, listener: (...args: any[]) => void) => import("@loopback/core").Application;
|
|
91
|
+
prependOnceListener: (event: string | symbol, listener: (...args: any[]) => void) => import("@loopback/core").Application;
|
|
92
|
+
eventNames: () => (string | symbol)[];
|
|
93
|
+
}) & typeof RestApplication;
|
|
94
|
+
export declare class GreetingApplication extends GreetingApplication_base {
|
|
95
|
+
constructor(config?: ApplicationConfig);
|
|
96
|
+
main(): Promise<void>;
|
|
97
|
+
}
|
|
98
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright IBM Corp. 2019. All Rights Reserved.
|
|
3
|
+
// Node module: @loopback/example-greeting-app
|
|
4
|
+
// This file is licensed under the MIT License.
|
|
5
|
+
// License text available at https://opensource.org/licenses/MIT
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.GreetingApplication = void 0;
|
|
8
|
+
const boot_1 = require("@loopback/boot");
|
|
9
|
+
const core_1 = require("@loopback/core");
|
|
10
|
+
const example_greeter_extension_1 = require("@loopback/example-greeter-extension");
|
|
11
|
+
const rest_1 = require("@loopback/rest");
|
|
12
|
+
const caching_service_1 = require("./caching-service");
|
|
13
|
+
const interceptors_1 = require("./interceptors");
|
|
14
|
+
const keys_1 = require("./keys");
|
|
15
|
+
class GreetingApplication extends boot_1.BootMixin(rest_1.RestApplication) {
|
|
16
|
+
constructor(config = {}) {
|
|
17
|
+
super(config);
|
|
18
|
+
this.projectRoot = __dirname;
|
|
19
|
+
this.add(core_1.createBindingFromClass(caching_service_1.CachingService, { key: keys_1.CACHING_SERVICE }));
|
|
20
|
+
this.add(core_1.createBindingFromClass(interceptors_1.CachingInterceptor));
|
|
21
|
+
this.component(example_greeter_extension_1.GreetingComponent);
|
|
22
|
+
}
|
|
23
|
+
async main() {
|
|
24
|
+
await this.boot();
|
|
25
|
+
await this.start();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.GreetingApplication = GreetingApplication;
|
|
29
|
+
//# sourceMappingURL=application.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"application.js","sourceRoot":"","sources":["../src/application.ts"],"names":[],"mappings":";AAAA,iDAAiD;AACjD,8CAA8C;AAC9C,+CAA+C;AAC/C,gEAAgE;;;AAEhE,yCAAyC;AACzC,yCAAyE;AACzE,mFAAsE;AACtE,yCAA+C;AAC/C,uDAAiD;AACjD,iDAAkD;AAClD,iCAAuC;AAEvC,MAAa,mBAAoB,SAAQ,gBAAS,CAAC,sBAAe,CAAC;IACjE,YAAY,SAA4B,EAAE;QACxC,KAAK,CAAC,MAAM,CAAC,CAAC;QACd,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;QAC7B,IAAI,CAAC,GAAG,CAAC,6BAAsB,CAAC,gCAAc,EAAE,EAAC,GAAG,EAAE,sBAAe,EAAC,CAAC,CAAC,CAAC;QACzE,IAAI,CAAC,GAAG,CAAC,6BAAsB,CAAC,iCAAkB,CAAC,CAAC,CAAC;QACrD,IAAI,CAAC,SAAS,CAAC,6CAAiB,CAAC,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,IAAI;QACR,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACF;AAbD,kDAaC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { ContextView } from '@loopback/core';
|
|
2
|
+
import { Message } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Configuration for CachingService
|
|
5
|
+
*/
|
|
6
|
+
export interface CachingServiceOptions {
|
|
7
|
+
ttl: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Message caching service
|
|
11
|
+
*/
|
|
12
|
+
export declare class CachingService {
|
|
13
|
+
private optionsView;
|
|
14
|
+
private timer;
|
|
15
|
+
private store;
|
|
16
|
+
constructor(optionsView: ContextView<CachingServiceOptions>);
|
|
17
|
+
/**
|
|
18
|
+
* Store a message in the cache
|
|
19
|
+
* @param key - Key for caching
|
|
20
|
+
* @param message - Message
|
|
21
|
+
*/
|
|
22
|
+
set(key: string, message: Message): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Load a message from the cache by key
|
|
25
|
+
* @param key - Key for caching
|
|
26
|
+
*/
|
|
27
|
+
get(key: string): Promise<Message | undefined>;
|
|
28
|
+
/**
|
|
29
|
+
* Delete a message from the cache by key
|
|
30
|
+
* @param key - Key for caching
|
|
31
|
+
*/
|
|
32
|
+
delete(key: string): Promise<boolean>;
|
|
33
|
+
/**
|
|
34
|
+
* Clear the cache
|
|
35
|
+
*/
|
|
36
|
+
clear(): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Check if the cached item is expired by key
|
|
39
|
+
* @param key - Key for caching
|
|
40
|
+
* @param now - The current date
|
|
41
|
+
*/
|
|
42
|
+
isExpired(key: string, now?: Date): Promise<boolean>;
|
|
43
|
+
/**
|
|
44
|
+
* Get the TTL setting
|
|
45
|
+
*/
|
|
46
|
+
getTTL(): Promise<number>;
|
|
47
|
+
/**
|
|
48
|
+
* Remove expired items from the cache
|
|
49
|
+
*/
|
|
50
|
+
sweep(): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* This method will be invoked when the application starts
|
|
53
|
+
*/
|
|
54
|
+
start(): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* This method will be invoked when the application stops
|
|
57
|
+
*/
|
|
58
|
+
stop(): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* This method may be used to restart the service (and may be triggered by a
|
|
61
|
+
* 'refresh' event)
|
|
62
|
+
*/
|
|
63
|
+
restart(): Promise<void>;
|
|
64
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright IBM Corp. 2019. All Rights Reserved.
|
|
3
|
+
// Node module: @loopback/example-greeting-app
|
|
4
|
+
// This file is licensed under the MIT License.
|
|
5
|
+
// License text available at https://opensource.org/licenses/MIT
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.CachingService = void 0;
|
|
8
|
+
const tslib_1 = require("tslib");
|
|
9
|
+
const core_1 = require("@loopback/core");
|
|
10
|
+
const debug_1 = tslib_1.__importDefault(require("debug"));
|
|
11
|
+
const debug = debug_1.default('greeter-extension');
|
|
12
|
+
/**
|
|
13
|
+
* Message caching service
|
|
14
|
+
*/
|
|
15
|
+
let CachingService = class CachingService {
|
|
16
|
+
constructor(optionsView) {
|
|
17
|
+
this.optionsView = optionsView;
|
|
18
|
+
this.store = new Map();
|
|
19
|
+
// Use a view so that we can listen on `refresh` events, which are emitted
|
|
20
|
+
// when the configuration binding is updated in the context.
|
|
21
|
+
optionsView.on('refresh', () => {
|
|
22
|
+
debug('Restarting the service as configuration changes...');
|
|
23
|
+
this.restart().catch(err => {
|
|
24
|
+
console.error('Cannot restart the caching service.', err);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
// We intentionally mark all operations `async` to reflect the commonly used
|
|
30
|
+
// caching infrastructure even though our in-memory implementation is based
|
|
31
|
+
// on a `Map` that provides synchronous APIs.
|
|
32
|
+
/**
|
|
33
|
+
* Store a message in the cache
|
|
34
|
+
* @param key - Key for caching
|
|
35
|
+
* @param message - Message
|
|
36
|
+
*/
|
|
37
|
+
async set(key, message) {
|
|
38
|
+
this.store.set(key, message);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Load a message from the cache by key
|
|
42
|
+
* @param key - Key for caching
|
|
43
|
+
*/
|
|
44
|
+
async get(key) {
|
|
45
|
+
const expired = await this.isExpired(key);
|
|
46
|
+
debug('Getting cache for %s', key, expired);
|
|
47
|
+
return expired ? undefined : this.store.get(key);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Delete a message from the cache by key
|
|
51
|
+
* @param key - Key for caching
|
|
52
|
+
*/
|
|
53
|
+
async delete(key) {
|
|
54
|
+
return this.store.delete(key);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Clear the cache
|
|
58
|
+
*/
|
|
59
|
+
async clear() {
|
|
60
|
+
this.store.clear();
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Check if the cached item is expired by key
|
|
64
|
+
* @param key - Key for caching
|
|
65
|
+
* @param now - The current date
|
|
66
|
+
*/
|
|
67
|
+
async isExpired(key, now = new Date()) {
|
|
68
|
+
const ttl = await this.getTTL();
|
|
69
|
+
const msg = this.store.get(key);
|
|
70
|
+
if (!msg)
|
|
71
|
+
return true;
|
|
72
|
+
return now.getTime() - msg.timestamp.getTime() > ttl;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get the TTL setting
|
|
76
|
+
*/
|
|
77
|
+
async getTTL() {
|
|
78
|
+
var _a;
|
|
79
|
+
const options = await this.optionsView.singleValue();
|
|
80
|
+
debug('Caching options: %j', options);
|
|
81
|
+
return (_a = options === null || options === void 0 ? void 0 : options.ttl) !== null && _a !== void 0 ? _a : 5000;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Remove expired items from the cache
|
|
85
|
+
*/
|
|
86
|
+
async sweep() {
|
|
87
|
+
debug('Sweeping cache...');
|
|
88
|
+
for (const key of this.store.keys()) {
|
|
89
|
+
if (await this.isExpired(key)) {
|
|
90
|
+
debug('Cache for %s is swept.', key);
|
|
91
|
+
await this.delete(key);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* This method will be invoked when the application starts
|
|
97
|
+
*/
|
|
98
|
+
async start() {
|
|
99
|
+
debug('Starting caching service');
|
|
100
|
+
await this.clear();
|
|
101
|
+
const ttl = await this.getTTL();
|
|
102
|
+
debug('TTL: %d', ttl);
|
|
103
|
+
this.timer = setInterval(() => {
|
|
104
|
+
this.sweep().catch(console.warn);
|
|
105
|
+
}, ttl);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* This method will be invoked when the application stops
|
|
109
|
+
*/
|
|
110
|
+
async stop() {
|
|
111
|
+
debug('Stopping caching service');
|
|
112
|
+
/* istanbul ignore if */
|
|
113
|
+
if (this.timer) {
|
|
114
|
+
clearInterval(this.timer);
|
|
115
|
+
}
|
|
116
|
+
await this.clear();
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* This method may be used to restart the service (and may be triggered by a
|
|
120
|
+
* 'refresh' event)
|
|
121
|
+
*/
|
|
122
|
+
async restart() {
|
|
123
|
+
await this.stop();
|
|
124
|
+
await this.start();
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
CachingService = tslib_1.__decorate([
|
|
128
|
+
core_1.injectable({ scope: core_1.BindingScope.SINGLETON }),
|
|
129
|
+
tslib_1.__param(0, core_1.config.view()),
|
|
130
|
+
tslib_1.__metadata("design:paramtypes", [core_1.ContextView])
|
|
131
|
+
], CachingService);
|
|
132
|
+
exports.CachingService = CachingService;
|
|
133
|
+
//# sourceMappingURL=caching-service.js.map
|