Как использовать машинописные прототипы сгенерированных файлов в угловом приложении gRPC - PullRequest
0 голосов
/ 05 октября 2019

Подведите итог проблемы

Я изучаю, как использовать gRPC, и хотел попробовать установить соединение клиент-сервер. Сервер (в Elixir) работает, хотя у меня было несколько проблем. Но так как я в основном бэкэнд-разработчик, у меня больше проблем с его внедрением в Angular, и я был бы признателен за помощь.

Я использую Angular 8.2.9 , Angular CLI 8.3.8 и Узел 10.16.0 для этого проекта.

Что я уже сделал?

  1. Создан новый угловойПроект cli с ng new test-grpc
  2. Сгенерировал модуль и компонент с ng g... и изменил базовую маршрутизацию, чтобы иметь рабочие страницы и URL.
  3. Установленные библиотеки Protobuf и gRPC npm install @improbable-eng/grpc-web @types/google-protobuf google-protobuf grpc-web-client protoc ts-protoc-gen --save
  4. Взял файл .proto из моего кода Elixir и скопировал его в папку src/proto
  5. Добавил команду protoc в package.json scripts: "protoc": "protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts.cmd --js_out=import_style=commonjs,binary:src/app/proto-gen --ts_out=service=true:src/app/proto-ts -I ./src/proto/ ./src/proto/*.proto"
  6. Вызывается команда для генерации файлов js и ts, найденных в src/app/proto-ts и src/app/proto-js
  7. Заменяет любые _ на - в именах сгенерированных файлов.
  8. Затем я попытался добавить следующий код в ngOnInit: https://github.com/improbable-eng/grpc-web/tree/master/client/grpc-web#usage-overview, например:
const getBookRequest = new GetBookRequest();
getBookRequest.setIsbn(60929871);
grpc.unary(BookService.GetBook, {
  request: getBookRequest,
  host: host,
  onEnd: res => {
    const { status, statusMessage, headers, message, trailers } = res;
    if (status === grpc.Code.OK && message) {
      console.log("all ok. got book: ", message.toObject());
    }
  }
});

Я пытался:

  • Чтобы создать Запрос в аргументах конструктора.
  • Чтобы найти, как добавить файлы машинописного текста (я только нахожу способы добавить файлы javascript в угловой проект, и сгенерированный JavaScript таков. тяжелее машинописного текста и не имеет никакого объяснения).
  • Чтобы понять, что такое jspb.Message (я до сих пор не знаю, но я думаю, что он связан с этим файлом: https://github.com/protocolbuffers/protobuf/blob/master/js/message.js)
  • Попытка найти руководство по реализации rGPD в угловом приложении (404 не найдено)

Файл .proto, два сгенерированных машинописных файла и компонент, пытающийся их использовать

Файл .proto ():

// src/proto/user.proto
syntax = "proto3";

service UserService {
  rpc ListUsers (ListUsersRequest) returns (ListUsersReply);
}

message ListUsersRequest {
  string message = 1;
}


message ListUsersReply {
  repeated User users = 1;
}

message User {
  int32 id = 1;
  string firstname = 2;
}

Сгенерированный машинописный код (2 файла):

// src/app/proto-ts/user-pb.d.ts

import * as jspb from "google-protobuf";

export class ListUsersRequest extends jspb.Message {
  getMessage(): string;
  setMessage(value: string): void;

  serializeBinary(): Uint8Array;
  toObject(includeInstance?: boolean): ListUsersRequest.AsObject;
  static toObject(includeInstance: boolean, msg: ListUsersRequest): ListUsersRequest.AsObject;
  static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
  static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
  static serializeBinaryToWriter(message: ListUsersRequest, writer: jspb.BinaryWriter): void;
  static deserializeBinary(bytes: Uint8Array): ListUsersRequest;
  static deserializeBinaryFromReader(message: ListUsersRequest, reader: jspb.BinaryReader): ListUsersRequest;
}

export namespace ListUsersRequest {
  export type AsObject = {
    message: string,
  }
}

export class ListUsersReply extends jspb.Message {
  clearUsersList(): void;
  getUsersList(): Array<User>;
  setUsersList(value: Array<User>): void;
  addUsers(value?: User, index?: number): User;

  serializeBinary(): Uint8Array;
  toObject(includeInstance?: boolean): ListUsersReply.AsObject;
  static toObject(includeInstance: boolean, msg: ListUsersReply): ListUsersReply.AsObject;
  static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
  static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
  static serializeBinaryToWriter(message: ListUsersReply, writer: jspb.BinaryWriter): void;
  static deserializeBinary(bytes: Uint8Array): ListUsersReply;
  static deserializeBinaryFromReader(message: ListUsersReply, reader: jspb.BinaryReader): ListUsersReply;
}

export namespace ListUsersReply {
  export type AsObject = {
    usersList: Array<User.AsObject>,
  }
}

export namespace User {
  export type AsObject = {
    id: number,
    firstname: string,
  }
}
// src/app/proto-ts/user-pb-service.d.ts
import * as user_pb from "./user-pb";
import {grpc} from "@improbable-eng/grpc-web";

type UserServiceListUsers = {
  readonly methodName: string;
  readonly service: typeof UserService;
  readonly requestStream: false;
  readonly responseStream: false;
  readonly requestType: typeof user_pb.ListUsersRequest;
  readonly responseType: typeof user_pb.ListUsersReply;
};

export class UserService {
  static readonly serviceName: string;
  static readonly ListUsers: UserServiceListUsers;
}

export type ServiceError = { message: string, code: number; metadata: grpc.Metadata }
export type Status = { details: string, code: number; metadata: grpc.Metadata }
interface UnaryResponse {
  cancel(): void;
}
interface ResponseStream<T> {
  cancel(): void;
  on(type: 'data', handler: (message: T) => void): ResponseStream<T>;
  on(type: 'end', handler: (status?: Status) => void): ResponseStream<T>;
  on(type: 'status', handler: (status: Status) => void): ResponseStream<T>;
}
interface RequestStream<T> {
  write(message: T): RequestStream<T>;
  end(): void;
  cancel(): void;
  on(type: 'end', handler: (status?: Status) => void): RequestStream<T>;
  on(type: 'status', handler: (status: Status) => void): RequestStream<T>;
}
interface BidirectionalStream<ReqT, ResT> {
  write(message: ReqT): BidirectionalStream<ReqT, ResT>;
  end(): void;
  cancel(): void;
  on(type: 'data', handler: (message: ResT) => void): BidirectionalStream<ReqT, ResT>;
  on(type: 'end', handler: (status?: Status) => void): BidirectionalStream<ReqT, ResT>;
  on(type: 'status', handler: (status: Status) => void): BidirectionalStream<ReqT, ResT>;
}

export class UserServiceClient {
  readonly serviceHost: string;

  constructor(serviceHost: string, options?: grpc.RpcOptions);
  listUsers(
    requestMessage: user_pb.ListUsersRequest,
    metadata: grpc.Metadata,
    callback: (error: ServiceError|null, responseMessage: user_pb.ListUsersReply|null) => void
  ): UnaryResponse;
  listUsers(
    requestMessage: user_pb.ListUsersRequest,
    callback: (error: ServiceError|null, responseMessage: user_pb.ListUsersReply|null) => void
  ): UnaryResponse;
}

Попытка использовать их в компонентеfile:

// src/app/modules/user/pages/list/list.component.ts
import { Component, OnInit } from '@angular/core';
import { grpc } from "@improbable-eng/grpc-web";

import { UserService } from '../../../../proto-ts/user-pb-service.d'
import { ListUsersRequest } from '../../../../proto-ts/user-pb.d'

@Component({
  selector: 'app-list',
  templateUrl: './list.component.html',
  styleUrls: ['./list.component.scss']
})
export class ListComponent implements OnInit {

  constructor() { }

  ngOnInit() {
    const listUsersRequest = new ListUsersRequest();
    listUsersRequest.setMessage("Hello world");

    grpc.unary(UserService.ListUsers, {
      request: listUsersRequest,
      host: "0.0.0.0:50051",
      onEnd: res => {
        const { status, statusMessage, headers, message, trailers } = res;
        if (status === grpc.Code.OK && message) {
          console.log("all ok. got the user list: ", message.toObject());
        } else {
          console.log("error");
        }
      }
    });
  }
}

Ожидаемый результат и фактические результаты

Я ожидаю, что смогу использовать неугловой машинописный код в компоненте (или услуге). Поскольку код выше (но файл компонента) генерируется, он не долженизменить , потому что любые изменения в файле .proto будут перезаписывать любые изменения, сделанные в этих двух сгенерированных файлах.

На данный момент я заблокирован этими двумя сообщениями об ошибках, которые появляются в зависимости от того, какой файлЯ сохранил последнее: TypeError: _proto_ts_user_pb_d__WEBPACK_IMPORTED_MODULE_4__.ListUsersRequest is not a constructor (если я сохраняю файл, сгенерированный protobuf последним, ошибки в консоли нет) или src\app\proto-ts\user-pb-service.d.ts and src\app\proto-ts\user-pb.d.ts is missing from the TypeScript compilation. Please make sure it is in your tsconfig via the 'files' or 'include' property. (если я сохранил угловой файл последним, то же сообщение об ошибке в консоли).

Добавление 'files' или 'include' в tsconfig.json (которое генерируется угловым CLI без каких-либо изменений) создает другую ошибку: Refused to load the image 'http://localhost:4200/favicon.ico' because it violates the following Content Security Policy directive: "default-src 'none'". Note that 'img-src' was not explicitly set, so 'default-src' is used as a fallback.

...