Я поделюсь своей реализацией с сообществом, потому что, хотя ответ Мирослав Байтош является ключом к этому, предлагаемая документация не очень ясна, я имею в виду Вызов другого APISи веб-сервисы .
- Прежде всего Я создаю новый источник данных с помощью инструмента команды
lb4
cli и заполняю файл json
тем жеключи, которые я использовал в источнике данных loopback 3.
// storage.datasource.ts
import { inject } from '@loopback/core';
import { juggler } from '@loopback/service-proxy';
import * as config from './storage-gc.datasource.json';
export class StorageGCDataSource extends juggler.DataSource {
static dataSourceName = 'StorageGC';
constructor(
@inject('datasources.config.StorageGC', { optional: true })
dsConfig: object = config,
) {
super(dsConfig);
}
}
// storage.datasource.json
{
"name": "Storage",
"connector": "loopback-component-storage",
"provider": "google",
"keyFilename": "your-project-key.json",
"projectId": "your-project-id",
"nameConflict": "makeUnique"
}
// Note: your-project-key.json is in the root folder at the same level that tsconfig.json
Помните, что вам необходимо установить loopback-component-storage running
npm install --save loopback-component-storage
- Во-вторых Я создаю две новые модели с помощью
lb4
команды cli.Это следующие модели: Container
и File
.
// container.model.ts
import {Entity, model, property} from '@loopback/repository';
@model()
export class Container extends Entity {
@property({
type: 'string',
required: true,
})
name: string;
constructor(data?: Partial<Container>) {
super(data);
}
}
// file.model.ts
import { Entity, model, property } from '@loopback/repository';
@model()
export class File extends Entity {
@property({
type: 'string',
required: true,
})
name: string;
@property({
type: 'string',
})
type?: string;
@property({
type: 'string',
})
url?: string;
constructor(data?: Partial<File>) {
super(data);
}
}
- На третьем месте Я создаю новую папку в каталоге
src/
называется interfaces
.В этой новой папке я создаю index.ts
(в основном для соблюдения соглашения об экспорте) и storage.interface.ts
(это важный файл)
// index.ts
export * from './storage.interface';
// storage.interface.ts
import { Container, File } from "../models";
export type Callback<T> = (err: Error | null, reply: T) => void;
export interface IStorageService {
// container methods
createContainer(container: Partial<Container>, cb: Callback<Container>): void;
destroyContainer(containerName: string, cb: Callback<boolean>): void;
getContainers(cb: Callback<Container[]>): void;
getContainer(containerName: string, cb: Callback<Container>): void;
// file methods
getFiles(containerName: string, options: Object, cb: Callback<File[]>): void;
getFile(containerName: string, fileName: string, cb: Callback<File>): void;
removeFile(containerName: string, fileName: string, cb: Callback<boolean>): void;
// main methods
upload(containerName: string, req: any, res: any, options: Object, cb: Callback<any>): void;
download(containerName: string, fileName: string, req: any, res: any, cb: Callback<any>): void;
}
- В-четвертых Я создаю
container.controller.ts
файл, но перед этим шагом вам нужно установить @ loopback / service-proxy , выполнив следующую команду:
npm install --save @loopback/service-proxy
// storage-gc.controller.ts
import { inject } from '@loopback/core';
import { serviceProxy } from '@loopback/service-proxy';
import { post, requestBody, del, param, get, getFilterSchemaFor, Request, Response, RestBindings } from '@loopback/rest';
import { Filter } from '@loopback/repository';
import { promisify } from 'util';
import { IStorageService } from '../interfaces';
import { Container, File } from '../models';
export class StorageGcController {
@serviceProxy('StorageGC') // StorageGC is the name of the datasoruce
private storageGcSvc: IStorageService;
constructor(@inject(RestBindings.Http.REQUEST) public request: Request,
@inject(RestBindings.Http.RESPONSE) public response: Response) { }
@post('/containers', {
responses: {
'200': {
description: 'Container model instance',
content: { 'application/json': { schema: { 'x-ts-type': Container } } },
},
},
})
async createContainer(@requestBody() container: Container): Promise<Container> {
const createContainer = promisify(this.storageGcSvc.createContainer);
return await createContainer(container);
}
@get('/containers', {
responses: {
'200': {
description: 'Array of Containers model instances',
content: {
'application/json': {
schema: { type: 'array', items: { 'x-ts-type': Container } },
},
},
},
},
})
async findContainer(@param.query.object('filter', getFilterSchemaFor(Container)) filter?: Filter): Promise<Container[]> {
const getContainers = promisify(this.storageGcSvc.getContainers);
return await getContainers();
}
@get('/containers/{containerName}', {
responses: {
'200': {
description: 'Container model instance',
content: { 'application/json': { schema: { 'x-ts-type': Container } } },
},
},
})
async findContainerByName(@param.path.string('containerName') containerName: string): Promise<Container> {
const getContainer = promisify(this.storageGcSvc.getContainer);
return await getContainer(containerName);
}
@del('/containers/{containerName}', {
responses: {
'204': {
description: 'Container DELETE success',
},
},
})
async deleteContainerByName(@param.path.string('containerName') containerName: string): Promise<boolean> {
const destroyContainer = promisify(this.storageGcSvc.destroyContainer);
return await destroyContainer(containerName);
}
@get('/containers/{containerName}/files', {
responses: {
'200': {
description: 'Array of Files model instances belongs to container',
content: {
'application/json': {
schema: { type: 'array', items: { 'x-ts-type': File } },
},
},
},
},
})
async findFilesInContainer(@param.path.string('containerName') containerName: string,
@param.query.object('filter', getFilterSchemaFor(Container)) filter?: Filter): Promise<File[]> {
const getFiles = promisify(this.storageGcSvc.getFiles);
return await getFiles(containerName, {});
}
@get('/containers/{containerName}/files/{fileName}', {
responses: {
'200': {
description: 'File model instances belongs to container',
content: { 'application/json': { schema: { 'x-ts-type': File } } },
},
},
})
async findFileInContainer(@param.path.string('containerName') containerName: string,
@param.path.string('fileName') fileName: string): Promise<File> {
const getFile = promisify(this.storageGcSvc.getFile);
return await getFile(containerName, fileName);
}
@del('/containers/{containerName}/files/{fileName}', {
responses: {
'204': {
description: 'File DELETE from Container success',
},
},
})
async deleteFileInContainer(@param.path.string('containerName') containerName: string,
@param.path.string('fileName') fileName: string): Promise<boolean> {
const removeFile = promisify(this.storageGcSvc.removeFile);
return await removeFile(containerName, fileName);
}
@post('/containers/{containerName}/upload', {
responses: {
'200': {
description: 'Upload a Files model instances into Container',
content: { 'application/json': { schema: { 'x-ts-type': File } } },
},
},
})
async upload(@param.path.string('containerName') containerName: string): Promise<File> {
const upload = promisify(this.storageGcSvc.upload);
return await upload(containerName, this.request, this.response, {});
}
@get('/containers/{containerName}/download/{fileName}', {
responses: {
'200': {
description: 'Download a File within specified Container',
content: { 'application/json': { schema: { 'x-ts-type': Object } } },
},
},
})
async download(@param.path.string('containerName') containerName: string,
@param.path.string('fileName') fileName: string): Promise<any> {
const download = promisify(this.storageGcSvc.download);
return await download(containerName, fileName, this.request, this.response);
}
}
С этими шагами ваш компонент хранения или разъем наиболее точно должен работать как положено.Но, следуя инструкциям Вызов руководства APIS и веб-служб , чтобы улучшить эту реализацию для интеграционных тестов, вы должны использовать провайдера вместо @serviceProxy
декоратора в контроллере, для этого я создаю новую папку с именем providers
внутри папки src/
со следующими двумя файлами:
// index.ts
export * from './storage-service.provider';
// storage-gc-service.provider.ts
import { getService, juggler } from '@loopback/service-proxy';
import { Provider } from '@loopback/core';
import { StorageGCDataSource } from '../datasources/storage-gc.datasource';
import { IStorageService } from '../interfaces';
export class StorageGCServiceProvider implements Provider<IStorageService> {
constructor(
protected dataSource: juggler.DataSource = new StorageGCDataSource()
/* I try to change the line above in the same way that documentation show,
as follows:
@inject('datasources.StorageGC')
protected dataSource: juggler.DataSource = new StorageGCDataSource()
and also, in the same way that repositories
@inject('datasources.StorageGC')
protected dataSource: StorageGCDataSource
but always return:
`Error: Cannot resolve injected arguments for StorageGCServiceProvider.[0]:
The arguments[0] is not decorated for dependency injection, but a value is
not supplied`
*/
) { }
value(): Promise<IStorageService> {
return getService(this.dataSource);
}
}
После этого вам необходимо изменить src/index.ts
и storage-gc.controller.ts
следующим образом
// src/index.ts
import { ApplicationConfig } from '@loopback/core';
import { HocicosCuriososApp } from './application';
import { StorageGCServiceProvider } from './providers';
export { HocicosCuriososApp };
export async function main(options: ApplicationConfig = {}) {
const app = new HocicosCuriososApp(options);
/* Add this line, it add a service to the app after that you can
call them in the controller with dependency injection, like:
@inject('services.StorageGCService') */
app.serviceProvider(StorageGCServiceProvider);
await app.boot();
await app.start();
const url = app.restServer.url;
console.log(`Server is running at ${url}`);
console.log(`Try ${url}/ping`);
return app;
}
// storage-gc.controller.ts
import { inject } from '@loopback/core';
import { post, requestBody, del, param, get, getFilterSchemaFor, Request, Response, RestBindings } from '@loopback/rest';
import { Filter } from '@loopback/repository';
import { promisify } from 'util';
import { IStorageService } from '../interfaces';
import { Container, File } from '../models';
export class StorageGcController {
@inject('services.StorageGCService')
private storageGcSvc: IStorageService;
constructor(@inject(RestBindings.Http.REQUEST) public request: Request,
@inject(RestBindings.Http.RESPONSE) public response: Response) { }
@post('/containers', {
responses: {
'200': {
description: 'Container model instance',
content: { 'application/json': { schema: { 'x-ts-type': Container } } },
},
},
})
async createContainer(@requestBody() container: Container): Promise<Container> {
const createContainer = promisify(this.storageGcSvc.createContainer);
return await createContainer(container);
}
@get('/containers', {
responses: {
'200': {
description: 'Array of Containers model instances',
content: {
'application/json': {
schema: { type: 'array', items: { 'x-ts-type': Container } },
},
},
},
},
})
async findContainer(@param.query.object('filter', getFilterSchemaFor(Container)) filter?: Filter): Promise<Container[]> {
const getContainers = promisify(this.storageGcSvc.getContainers);
return await getContainers();
}
@get('/containers/{containerName}', {
responses: {
'200': {
description: 'Container model instance',
content: { 'application/json': { schema: { 'x-ts-type': Container } } },
},
},
})
async findContainerByName(@param.path.string('containerName') containerName: string): Promise<Container> {
const getContainer = promisify(this.storageGcSvc.getContainer);
return await getContainer(containerName);
}
@del('/containers/{containerName}', {
responses: {
'204': {
description: 'Container DELETE success',
},
},
})
async deleteContainerByName(@param.path.string('containerName') containerName: string): Promise<boolean> {
const destroyContainer = promisify(this.storageGcSvc.destroyContainer);
return await destroyContainer(containerName);
}
@get('/containers/{containerName}/files', {
responses: {
'200': {
description: 'Array of Files model instances belongs to container',
content: {
'application/json': {
schema: { type: 'array', items: { 'x-ts-type': File } },
},
},
},
},
})
async findFilesInContainer(@param.path.string('containerName') containerName: string,
@param.query.object('filter', getFilterSchemaFor(Container)) filter?: Filter): Promise<File[]> {
const getFiles = promisify(this.storageGcSvc.getFiles);
return await getFiles(containerName, {});
}
@get('/containers/{containerName}/files/{fileName}', {
responses: {
'200': {
description: 'File model instances belongs to container',
content: { 'application/json': { schema: { 'x-ts-type': File } } },
},
},
})
async findFileInContainer(@param.path.string('containerName') containerName: string,
@param.path.string('fileName') fileName: string): Promise<File> {
const getFile = promisify(this.storageGcSvc.getFile);
return await getFile(containerName, fileName);
}
@del('/containers/{containerName}/files/{fileName}', {
responses: {
'204': {
description: 'File DELETE from Container success',
},
},
})
async deleteFileInContainer(@param.path.string('containerName') containerName: string,
@param.path.string('fileName') fileName: string): Promise<boolean> {
const removeFile = promisify(this.storageGcSvc.removeFile);
return await removeFile(containerName, fileName);
}
@post('/containers/{containerName}/upload', {
responses: {
'200': {
description: 'Upload a Files model instances into Container',
content: { 'application/json': { schema: { 'x-ts-type': File } } },
},
},
})
async upload(@param.path.string('containerName') containerName: string): Promise<File> {
const upload = promisify(this.storageGcSvc.upload);
return await upload(containerName, this.request, this.response, {});
}
@get('/containers/{containerName}/download/{fileName}', {
responses: {
'200': {
description: 'Download a File within specified Container',
content: { 'application/json': { schema: { 'x-ts-type': Object } } },
},
},
})
async download(@param.path.string('containerName') containerName: string,
@param.path.string('fileName') fileName: string): Promise<any> {
const download = promisify(this.storageGcSvc.download);
return await download(containerName, fileName, this.request, this.response);
}
}
Это все, следуя этим шагам, вы должны очень хорошо работать с компонентом хранения.Удачи!
С уважением.