как использовать компоненты хранения в loopback 4.0? - PullRequest
0 голосов
/ 13 февраля 2019

Я использую компоненты хранилища в loopback 3.0 для доступа к облачным хранилищам.но мне нужно, как реализовать в loopback 4.0. Ниже ссылка, чтобы сделать образец в 3.0.

https://github.com/strongloop/loopback-example-storage

Ответы [ 2 ]

0 голосов
/ 18 марта 2019

Я поделюсь своей реализацией с сообществом, потому что, хотя ответ Мирослав Байтош является ключом к этому, предлагаемая документация не очень ясна, я имею в виду Вызов другого 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);
  }
}

Это все, следуя этим шагам, вы должны очень хорошо работать с компонентом хранения.Удачи!

С уважением.

0 голосов
/ 15 февраля 2019

Привет от команды LoopBack!

Мы не рассматривали интеграцию loopback-компонента-хранилища с LoopBack версии 4.

Поскольку компонент хранилища ведет себябольше похоже на разъем, чем на компонент, я думаю, что его можно использовать в LoopBack 4 через слой @loopback/service-proxy.См. Вызов других APIS и веб-сервисов для документации.Фрагменты кода на этой странице используют loopback-connector-rest в качестве примера, вместо этого вы будете использовать loopback-component-storage.

Как только вы сможете получить доступ к компоненту хранилища из JavaScript / TypeScript, вам также потребуется предоставить REST API.,LoopBack 4 не предоставляет никаких встроенных контроллеров, вам придется написать один самостоятельно.Смотрите определение удаленных методов LB3 в storage-service.js , вам нужно будет создать методы контроллера, чтобы принимать те же аргументы, а затем вызывать прокси-сервер службы под капотом.

Если вы хотите изучить эту интеграцию, то, вероятно, лучше всего открыть новый выпуск GitHub в loopback-next , где мы можем провести структурированный диалог.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...