Skip to content
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
2 changes: 2 additions & 0 deletions packages/boot/src/boot.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
RepositoryBooter,
DataSourceBooter,
ServiceBooter,
ApplicationMetadataBooter,
} from './booters';
import {BootBindings} from './keys';

Expand All @@ -23,6 +24,7 @@ export class BootComponent implements Component {
// Export a list of default booters in the component so they get bound
// automatically when this component is mounted.
booters = [
ApplicationMetadataBooter,
ControllerBooter,
RepositoryBooter,
ServiceBooter,
Expand Down
36 changes: 36 additions & 0 deletions packages/boot/src/booters/application-metadata.booter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright IBM Corp. 2018. All Rights Reserved.
// Node module: @loopback/boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {CoreBindings, Application} from '@loopback/core';
import {inject} from '@loopback/context';
import {BootBindings} from '../keys';
import {Booter} from '../interfaces';
import path = require('path');

import * as debugModule from 'debug';
const debug = debugModule('loopback:boot:booter:application-metadata');

/**
*
* Configure the application with metadata from `package.json`
*
* @param app Application instance
* @param projectRoot Root of User Project
*/
export class ApplicationMetadataBooter implements Booter {
constructor(
@inject(CoreBindings.APPLICATION_INSTANCE) public app: Application,
@inject(BootBindings.PROJECT_ROOT) private projectRoot: string,
) {}

async configure() {
try {
const pkg = require(path.resolve(this.projectRoot, 'package.json'));
this.app.setMetadata(pkg);
} catch (err) {
debug('package.json not found', err);
}
}
}
1 change: 1 addition & 0 deletions packages/boot/src/booters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export * from './controller.booter';
export * from './datasource.booter';
export * from './repository.booter';
export * from './service.booter';
export * from './application-metadata.booter';
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright IBM Corp. 2018. All Rights Reserved.
// Node module: @loopback/boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {givenHttpServerConfig, TestSandbox, expect} from '@loopback/testlab';
import {resolve} from 'path';
import {BooterApp} from '../fixtures/application';
import {CoreBindings} from '@loopback/core';

describe('application metadata booter acceptance tests', () => {
let app: BooterApp;
const SANDBOX_PATH = resolve(__dirname, '../../.sandbox');
const sandbox = new TestSandbox(SANDBOX_PATH);

beforeEach('reset sandbox', () => sandbox.reset());
beforeEach(getApp);

afterEach(stopApp);

it('binds content of package.json to application metadata', async () => {
await app.boot();
const metadata = await app.get(CoreBindings.APPLICATION_METADATA);
expect(metadata).containEql({
name: 'boot-test-app',
version: '1.0.0',
description: 'boot-test-app',
});
});

async function getApp() {
await sandbox.copyFile(resolve(__dirname, '../fixtures/package.json'));
await sandbox.copyFile(resolve(__dirname, '../fixtures/application.js'));

const MyApp = require(resolve(SANDBOX_PATH, 'application.js')).BooterApp;
app = new MyApp({
rest: givenHttpServerConfig(),
});
}

async function stopApp() {
try {
await app.stop();
} catch (err) {
console.log(`Stopping the app threw an error: ${err}`);
}
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ describe('controller booter acceptance tests', () => {
});

async function getApp() {
await sandbox.copyFile(resolve(__dirname, '../fixtures/package.json'));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will not work after the changes I made in #1824) - the package.json file is not going to be copied over from test to dist/test.

I think the easiest solution is to load the package json file from test/fixtures/application.ts:

import * as pkg from './package.json'

await sandbox.copyFile(resolve(__dirname, '../fixtures/application.js'));
await sandbox.copyFile(
resolve(__dirname, '../fixtures/multiple.artifact.js'),
Expand Down
4 changes: 4 additions & 0 deletions packages/boot/test/fixtures/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import {RestApplication} from '@loopback/rest';
import {ServiceMixin} from '@loopback/service-proxy';
import {BootMixin} from '../..';

// Force package.json to be copied to `dist` by `tsc`
//tslint:disable-next-line:no-unused-variable
import * as pkg from './package.json';

export class BooterApp extends BootMixin(
ServiceMixin(RepositoryMixin(RestApplication)),
) {
Expand Down
19 changes: 19 additions & 0 deletions packages/boot/test/fixtures/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "boot-test-app",
"version": "1.0.0",
"description": "boot-test-app",
"keywords": [
"loopback-application",
"loopback"
],
"engines": {
"node": ">=8.9"
},
"scripts": {
},
"repository": {
"type": "git"
},
"author": "",
"license": ""
}
29 changes: 29 additions & 0 deletions packages/core/src/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,16 @@ export class Application extends Context {
const instance = this.getSync<Component>(componentKey);
mountComponent(this, instance);
}

/**
* Set application metadata. `@loopback/boot` calls this method to populate
* the metadata from `package.json`.
*
* @param metadata Application metadata
*/
public setMetadata(metadata: ApplicationMetadata) {
this.bind(CoreBindings.APPLICATION_METADATA).to(metadata);
}
}

/**
Expand All @@ -207,3 +217,22 @@ export interface ApplicationConfig {

// tslint:disable-next-line:no-any
export type ControllerClass = Constructor<any>;

/**
* Type definition for JSON
*/
export type JSONPrimitive = string | number | boolean | null;
export type JSONValue = JSONPrimitive | JSONObject | JSONArray;
export interface JSONObject {
[property: string]: JSONValue;
}
export interface JSONArray extends Array<JSONValue> {}

/**
* Type description for `package.json`
*/
export interface ApplicationMetadata extends JSONObject {
name: string;
version: string;
description: string;
}
9 changes: 8 additions & 1 deletion packages/core/src/keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// License text available at https://opensource.org/licenses/MIT

import {BindingKey} from '@loopback/context';
import {Application, ControllerClass} from './application';
import {Application, ControllerClass, ApplicationMetadata} from './application';

/**
* Namespace for core binding keys
Expand All @@ -25,6 +25,13 @@ export namespace CoreBindings {
'application.config',
);

/**
* Binding key for the content of `package.json`
*/
export const APPLICATION_METADATA = BindingKey.create<ApplicationMetadata>(
'application.metadata',
);

// server
/**
* Binding key for servers
Expand Down