问题描述
我正在研究nest.js微服务项目。定义了控制器和服务,后端运行无误。我正在尝试使用创建资源(在此示例中为subscriptionPlan) grpCurl,像这样:
grpcurl -d '{
"name": "Test GRPC","code": "12312","description": "test","price": 10,"invoicePeriod": 10,"invoiceDuration":"DAY"
}' -plaintext -proto rpc/rpc.proto 127.0.0.1:5000 rpc.SubscriptionPlanService/Create
当我运行此命令时,我收到一条错误消息:Failed to process proto source files.: Could not parse given files: %!v(PANIC=Error method: runtime error: invalid memory address or nil pointer dereference)
。
我觉得后端/服务代码中没有问题,这与项目设置有关,也许我缺少命令行工具库。我检查了是否安装了x代码开发工具,还安装了所有模块依赖项。
这是我的:subscription_plan.service.ts
/* eslint-disable complexity */
/* eslint-disable @typescript-eslint/no-unused-vars */
import { injectable } from 'inversify';
import _ from 'lodash';
import { createEverLogger } from '../../helpers/Log';
import { ErrorGenerator } from '../../shared/errors.generator';
import {
BadRequestError,ConflictError,NotFoundError,ParseError,} from '../../shared/errors.messages';
import { DatabaseService } from '../database/database.service';
import { servicesContainer } from '../inversify.config';
import { IService } from '../IService';
import { SubscriptionPlans } from './model/subscription_plan.model';
import {
DeleteSubscriptionPlanResponse,ISubscriptionPlanService,SubscriptionInputPayload,SubscriptionPlan,SubscriptionPlanFilter,SubscriptionPlanResponse,SubscriptionPlanUpdatePayload,UpdateSubscriptionPlanResponse,} from './types/subscription_plan.types';
import { subscriptionPlanCreateSchema } from './validators/subscription_plan.create.yup';
import { subscriptionPlanFilterSchema } from './validators/subscription_plan.filter.yup';
import { subscriptionPlanUpdateSchema } from './validators/subscription_plan.update.yup';
/**
* Subscription Plans Service
* CRUD operation for Subscription Plan
* @export
* @class SubscriptionPlanService
* @implements {ISubscriptionPlanService}
* @implements {IService}
*/
@injectable()
export class SubscriptionPlanService
implements ISubscriptionPlanService,IService {
private logger = createEverLogger({ name: 'SubscriptionPlanService' });
private dbService = servicesContainer.get<DatabaseService>(DatabaseService);
/**
* Create the subscription plan
*
* Returns the newly created subscription plan object with id
*
* @param {SubscriptionInputPayload} payload
* @returns {Promise<SubscriptionPlan>}
* @memberof SubscriptionPlanService
*/
async create(payload: SubscriptionInputPayload): Promise<SubscriptionPlan> {
let result: SubscriptionPlan;
try {
// Validate the payload
await subscriptionPlanCreateSchema.validate(payload,{
abortEarly: false,});
const slug = payload.name.toLowerCase().replace(' ','-');
// Check for existing slug
const isExist = await this.dbService.findOne<
SubscriptionPlan,SubscriptionPlanFilter
>({ slug });
if (!_.isNil(isExist)) {
throw ConflictError(ErrorGenerator.Duplicate('Subscription Plan'));
}
// Make db call
result = await this.dbService.create<SubscriptionPlan,SubscriptionPlans>(
new SubscriptionPlans({ ...payload,slug }),);
this.logger.debug('Subscription Plan added Successfully',result);
} catch (e) {
this.logger.error(e);
ParseError(e,ErrorGenerator.Duplicate('Subscription Plan'));
}
if (!_.isEmpty(result?.id)) {
return result;
}
throw BadRequestError(ErrorGenerator.UnableSave('Subscription Plan'));
}
/**
* Get the subscription plan by id only
* will return single object
* @param {SubscriptionPlanFilter} where
* @returns {Promise<SubscriptionPlan>}
* @memberof SubscriptionPlanService
*/
async findOne(where: SubscriptionPlanFilter): Promise<SubscriptionPlan> {
let edge: SubscriptionPlan;
try {
// Validate Input
await subscriptionPlanFilterSchema.validate(where,});
// Get the subscription plan id
// Todo: Implement other filters
const id = where?.id;
if (!_.isNil(id)) {
// make db call
edge = await this.dbService.findOne<
SubscriptionPlan,SubscriptionPlanFilter
>(new SubscriptionPlans({ id }));
}
} catch (e) {
this.logger.error(e);
ParseError(e,ErrorGenerator.NotFound('Subscription Plan'));
}
if (!_.isEmpty(edge)) {
this.logger.debug('Subscription Plan loaded Successfully',edge);
return edge;
}
throw NotFoundError(ErrorGenerator.NotFound('Subscription Plan'));
}
/**
* Get all the subscriptions plans
* with pagination
* @param {SubscriptionPlanFilter} [where]
* @returns {Promise<SubscriptionPlanResponse>}
* @memberof SubscriptionPlanService
*/
async findAll(
where?: SubscriptionPlanFilter,): Promise<SubscriptionPlanResponse> {
// Validate the Input
let edges: SubscriptionPlan[];
let count: number; // Rows counts
let recordLimit = 10; // Pagination Limit
let recordSkip = 0; // Pagination: SKIP
// Todo
// Transform from Object to Array
// { id: SortDirection.ASC } to [ "id","ASC"]
// for (const [key,value] of Object.entries(sortBy)) {
// sortOrder.push([key,value]);
// }
try {
await subscriptionPlanFilterSchema.validate(where,});
if (where) {
// Todo: Implement other filters
const { id,limit,skip } = where;
// isNil check for for null or undefined
if (!_.isNil(id) && !_.isNil(limit) && !_.isNil(skip)) {
// Set Limit and Skip for `page_info`
recordLimit = limit;
recordSkip = skip;
// Load the SubscriptionPlan with ID and Pagination
[edges,count] = await this.dbService.findAll<
SubscriptionPlan,Partial<SubscriptionPlanFilter>
>(new SubscriptionPlans({ id }),recordLimit,recordSkip);
} else if (!_.isNil(limit) && !_.isNil(skip)) {
// Set Limit and Skip for `page_info`
recordLimit = limit;
recordSkip = skip;
// Load All SubscriptionPlan with default pagination
[edges,Partial<SubscriptionPlanFilter>
>(new SubscriptionPlans(),recordSkip);
} else if (!_.isNil(id)) {
// Load All SubscriptionPlan with id with default pagination
[edges,recordSkip);
}
} else {
// Load All SubscriptionPlan with default pagination
[edges,count] = await this.dbService.findAll<
SubscriptionPlan,Partial<SubscriptionPlanFilter>
>(new SubscriptionPlans(),recordSkip);
}
} catch (error) {
this.logger.error(error);
// Empty
ParseError(error,ErrorGenerator.NotFound('Subscription Plan'));
}
// Validate edges are not empty
if (!_.isEmpty(edges)) {
this.logger.debug('Subscription Plan loaded Successfully',edges);
return {
edges,page_info: {
total: count,limit: recordLimit,skip: recordSkip,has_more: count > recordLimit + recordSkip ? true : false,},};
}
throw NotFoundError(ErrorGenerator.NotFound('Subscription Plan'));
}
count(where?: SubscriptionPlanFilter): Promise<number> {
throw new Error('Method not implemented.');
}
/**
* Update the subscription plan
* by id only
* @param {SubscriptionPlanUpdatePayload} payload
* @param {SubscriptionPlanFilter} where
* @returns {Promise<UpdateSubscriptionPlanResponse>}
* @memberof SubscriptionPlanService
*/
async update(
payload: SubscriptionPlanUpdatePayload,where: SubscriptionPlanFilter,): Promise<UpdateSubscriptionPlanResponse> {
let modified: number;
let edges: SubscriptionPlan[];
try {
// Validate the input
await subscriptionPlanUpdateSchema.validate(
{ ...payload,...where },{ abortEarly: false },);
// Check where is defined
if (where) {
const { id } = where;
// Get Subscription plan id
if (!_.isNil(id)) {
// Generate the slug
const slug = payload.name.toLowerCase().replace(' ','-');
// Check for existing slug
const isExist = await this.dbService.findOne<
SubscriptionPlan,SubscriptionPlanFilter
>({ slug });
// Validate the ID is not same
// Return document can have the same ID as of update
if (!_.isNil(isExist) && isExist?.id != id) {
throw ConflictError(ErrorGenerator.Duplicate('Subscription Plan'));
}
// Make db call
[edges,modified] = await this.dbService.update<
SubscriptionPlan,Partial<SubscriptionPlan>,SubscriptionPlanFilter
>(
new SubscriptionPlans({ ...payload,new SubscriptionPlans({ id }),);
this.logger.debug('Subscription Plan Update Successfully',edges);
}
}
} catch (e) {
this.logger.error(e);
ParseError(e,ErrorGenerator.Duplicate('Subscription Plan'));
}
if (modified > 0) {
// Return the update data with count
return { modified,edges };
}
throw NotFoundError(ErrorGenerator.NotFound('Subscription Plan'));
}
/**
* Delete the subscription plan
* by id only
* @param {SubscriptionPlanFilter} where
* @returns {Promise<DeleteSubscriptionPlanResponse>}
* @memberof SubscriptionPlanService
*/
async delete(
where: SubscriptionPlanFilter,): Promise<DeleteSubscriptionPlanResponse> {
let modified: number;
let edges: SubscriptionPlan[];
try {
this.logger.info(where,'Delete request');
// Validate the payload
await subscriptionPlanFilterSchema.validate(where,{ abortEarly: false });
// Check where is defined
if (where) {
// Get the subscription plan id
const { id } = where;
if (!_.isNil(id)) {
// Make db call
[edges,modified] = await this.dbService.delete<
SubscriptionPlan,SubscriptionPlanFilter
>(new SubscriptionPlans({ id }));
this.logger.debug('Subscription Plan deleted Successfully',ErrorGenerator.UnabletoDelete('Subscription Plan'));
}
if (modified > 0) {
return { modified,edges };
}
throw NotFoundError(ErrorGenerator.NotFound('Subscription Plan'));
}
}
这是我的service_plan.controller.ts
/* eslint-disable @typescript-eslint/no-unused-vars */
import { Controller } from '@nestjs/common';
import { Grpcmethod,RpcException } from '@nestjs/microservices';
import { HttpError } from 'http-json-errors';
import { inject,LazyServiceIdentifer } from 'inversify';
import { rpc,subscription_plan } from '../codegen/rpc';
import { SubscriptionPlanService } from '../services/SubscriptionPlan/subscription_plan.service';
import SubscriptionPlanResponse = subscription_plan.SubscriptionPlanResponse;
import SubscriptionPlanFilter = subscription_plan.SubscriptionPlanFilter;
import SubscriptionPlan = subscription_plan.SubscriptionPlan;
import DeleteSubscriptionPlanResponse = subscription_plan.DeleteSubscriptionPlanResponse;
import UpdateSubscriptionPlanResponse = subscription_plan.UpdateSubscriptionPlanResponse;
import UpdateSubscriptionPlanRequest = subscription_plan.UpdateSubscriptionPlanRequest;
import SubscriptionPlanInput = subscription_plan.SubscriptionPlanInput;
import IEmpty = rpc.IEmpty;
@Controller()
export class SubscriptionPlanController {
constructor(
@inject(new LazyServiceIdentifer(() => SubscriptionPlanService))
private readonly subscriptionPlanService: SubscriptionPlanService,) {}
/**
* Get all subscription plans
* Test command : grpcurl -plaintext -proto rpc/rpc.proto 127.0.0.1:5000 rpc.SubscriptionPlanService/FindAll
* @param {IEmpty} req
* @returns {Promise<SubscriptionPlans>}
* @memberof SubscriptionPlanController
*/
@Grpcmethod('SubscriptionPlanService','FindAll')
async findAll(req: IEmpty): Promise<SubscriptionPlanResponse> {
try {
const obj = await this.subscriptionPlanService.findAll();
return SubscriptionPlanResponse.create(
(obj as unkNown) as SubscriptionPlanResponse,);
} catch (error) {
const errorInfo = error as HttpError;
throw new RpcException({
code: errorInfo.statusCode,message: JSON.stringify(error),});
}
}
/**
* Get One subscription plan
* Test command : grpcurl -d '{"id":"513-A"}' -plaintext -proto rpc/rpc.proto 127.0.0.1:5000 rpc.SubscriptionPlanService/FindOne
* @param {SubscriptionPlanFilter} where
* @returns {Promise<SubscriptionPlan>}
* @memberof SubscriptionPlanController
*/
@Grpcmethod('SubscriptionPlanService','FindOne')
async findOne(where: SubscriptionPlanFilter): Promise<SubscriptionPlan> {
try {
const id = where?.id;
const obj = await this.subscriptionPlanService.findOne({ id });
return SubscriptionPlan.create((obj as unkNown) as SubscriptionPlan);
} catch (error) {
const errorInfo = error as HttpError;
throw new RpcException({
code: errorInfo.statusCode,});
}
}
/**
* Create subscription plan
* Test command : grpcurl -d '{
"name": "Test GRPC","invoiceDuration":"DAY"
}' -plaintext -proto rpc/rpc.proto 127.0.0.1:5000 rpc.SubscriptionPlanService/Create
*
* @param {SubscriptionPlanInput} payload
* @returns {Promise<SubscriptionPlan>}
* @memberof SubscriptionPlanController
*/
@Grpcmethod('SubscriptionPlanService','Create')
async create(payload: SubscriptionPlanInput): Promise<SubscriptionPlan> {
try {
const obj = await this.subscriptionPlanService.create({
name: payload?.name,price: payload?.price,invoice_duration: payload?.invoice_duration as any,invoice_period: payload?.invoice_period,trail_period: payload?.trail_period,trail_duration: payload?.trail_duration as any,description: payload?.description,code: payload?.code,});
return SubscriptionPlan.create((obj as unkNown) as SubscriptionPlan);
} catch (error) {
const errorInfo = error as HttpError;
throw new RpcException({
code: errorInfo.statusCode,message: JSON.stringify(error) || error,});
}
}
/**
* Update subscription plan
* Test command :
* grpcurl -d '{"payload":{"name":"Update Text"},"where":{"id":"97-A"}}'
* -plaintext -proto rpc/rpc.proto 127.0.0.1:5000 rpc.SubscriptionPlanService/Update
* @param {UpdateSubscriptionPlanRequest} data
* @returns {Promise<UpdateSubscriptionPlanResponse>}
* @memberof SubscriptionPlanController
*/
@Grpcmethod('SubscriptionPlanService','Update')
async update(
data: UpdateSubscriptionPlanRequest,): Promise<UpdateSubscriptionPlanResponse> {
try {
const { payload,where } = data;
const obj = await this.subscriptionPlanService.update(
payload as any,where,);
return UpdateSubscriptionPlanResponse.create(
(obj as unkNown) as UpdateSubscriptionPlanResponse,});
}
}
/**
* Delete subscription plan
* Test command : grpcurl -d '{"id":"513-A"}' -plaintext -proto rpc/rpc.proto 127.0.0.1:5000 rpc.SubscriptionPlanService/Delete
* @param {SubscriptionPlanFilter} where
* @returns {Promise<DeleteSubscriptionPlanResponse>}
* @memberof SubscriptionPlanController
*/
@Grpcmethod('SubscriptionPlanService','Delete')
async delete(
where: SubscriptionPlanFilter,): Promise<DeleteSubscriptionPlanResponse> {
try {
const id = where?.id;
const obj = await this.subscriptionPlanService.delete({ id });
return DeleteSubscriptionPlanResponse.create(
(obj as unkNown) as DeleteSubscriptionPlanResponse,});
}
}
}
我的app.module.ts
import { Module } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { APP_FILTER,APP_INTERCEPTOR } from '@nestjs/core';
import { GraphQLModule } from '@nestjs/graphql';
import GraphQLJSON,{ GraphQLJSONObject } from 'graphql-type-json';
import _ from 'lodash';
import { ConfigModule } from './config/config.module';
// import { servicesContainer } from './services/inversify.config';
import { ServicesModule } from './services/service.module';
// import { SubscriptionPlanService } from './services/SubscriptionPlan/subscription_plan.service';
import { HttpExceptionFilter } from './shared/exception-filter/http-exception.filter';
import { TimeoutInterceptor } from './shared/interceptor/timeout.interceptor';
import schemaDirectives from './shared/schema-directive/index';
import { SubscriptionPlanModule } from './subscription_plans/subscription_plan.module';
import { UserModule } from './users/user.module';
@Module({
imports: [
ConfigModule,ServicesModule,SubscriptionPlanModule,UserModule,GraphQLModule.forRootAsync({
useFactory: () => ({
schemaDirectives,include: [],typePaths: ['./**/**/*.graphql'],installSubscriptionHandlers: true,context: ({ req }) => ({ req }),introspection: true,// debug: configService.get<string>('app.nodeenv') === 'development',// engine: {
// schemaTag: configService.get<string>('app.nodeenv'),// apiKey: configService.get<string>('app.apolloEngineApiKey'),// },resolverValidationoptions: {
requireResolversForResolveType: false,resolvers: {
JSON: GraphQLJSON,JSONObject: GraphQLJSONObject,formatError: (error) => {
try {
error.message = JSON.parse(error.message);
} catch (e) {
// Empty
}
return {
...error,message: error.message,code: _.get(error,'extensions.exception.title','UNKNowN'),locations: error.locations,path: error.path,};
},formatResponse: (response) => response,}),inject: [ConfigService],],controllers: [],providers: [
{
provide: APP_INTERCEPTOR,useClass: TimeoutInterceptor,{
provide: APP_FILTER,useClass: HttpExceptionFilter,})
export class AppModule {
constructor() {
// Debug the Insert operation
// const s = servicesContainer.get<SubscriptionPlanService>(
// SubscriptionPlanService,// );
// void s.create({
// name: 'Test',// invoice_duration: 'DAY',// invoice_period: 30,// price: 10,// code: '12312',// description: 'test',// trail_duration: 'DAY',// trail_period: 12,// });
// void s.findAll();
// void s.delete({ id: '257-A' });
// void s.findOne({ id: '257-A' });
// void s.update({ name: 'Test Update name1' },{ id: '353-A' });
}
}
我觉得不必共享所有这些代码,但是如果有人需要任何信息,请在评论中让我知道。
即使您对可能引起上述问题的线索有很大的帮助。
解决方法
当服务器中的所有内容似乎都按预期运行时,我收到了相同的错误消息。原来我在错误的目录中调用了grpcurl。
基于参数-proto rpc/rpc.proto
,应检查自己是否位于rpc
的父目录中,然后尝试再次调用grpcurl。
gRPCurl项目已经有了一个suggestion to change the error message更好的项目。