From 90f44e668ec55a435dbfa54345e16a4b253fef0d Mon Sep 17 00:00:00 2001 From: PickHD Date: Wed, 17 May 2023 16:36:27 +0700 Subject: [PATCH 1/2] feat (SRS-12) : update proto, docs & implement API delete users shortener --- user/api/v1/proto/shortener/shortener.proto | 4 + user/cmd/v1/example.env | 1 + user/docs/docs.go | 54 +++++++++++ user/docs/swagger.json | 54 +++++++++++ user/docs/swagger.yaml | 36 +++++++ user/internal/v1/application/app.go | 2 +- user/internal/v1/config/config.go | 2 + user/internal/v1/controller/user.go | 35 ++++++- user/internal/v1/infrastructure/http.go | 2 + user/internal/v1/repository/user.go | 45 +++++++++ user/internal/v1/service/user.go | 14 +++ .../api/v1/proto/shortener/shortener.pb.go | 97 +++++++++++++++---- 12 files changed, 326 insertions(+), 20 deletions(-) diff --git a/user/api/v1/proto/shortener/shortener.proto b/user/api/v1/proto/shortener/shortener.proto index b5ede66..5f1183c 100644 --- a/user/api/v1/proto/shortener/shortener.proto +++ b/user/api/v1/proto/shortener/shortener.proto @@ -36,4 +36,8 @@ message UpdateVisitorCountMessage { message UpdateShortenerMessage { string id =1; string full_url=2; +} + +message DeleteShortenerMessage { + string id = 1; } \ No newline at end of file diff --git a/user/cmd/v1/example.env b/user/cmd/v1/example.env index 987bb10..9001aba 100644 --- a/user/cmd/v1/example.env +++ b/user/cmd/v1/example.env @@ -14,6 +14,7 @@ AMQP_QUEUE_CREATE_SHORTENER=create-shortener-queue AMQP_QUEUE_UPDATE_VISITOR_COUNT=update-visitor-count-queue AMQP_QUEUE_UPLOAD_AVATAR=upload-avatar-queue AMQP_QUEUE_UPDATE_SHORTENER=update-shortener-queue +AMQP_QUEUE_DELETE_SHORTENER=delete-shortener-queue JWT_SECRET=secret JWT_EXPIRE=7 diff --git a/user/docs/docs.go b/user/docs/docs.go index 1deeb1a..a831d98 100644 --- a/user/docs/docs.go +++ b/user/docs/docs.go @@ -297,6 +297,60 @@ const docTemplate = `{ } } } + }, + "delete": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "User" + ], + "summary": "Delete Users Short URL", + "parameters": [ + { + "type": "string", + "description": "id short urls", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Authorization Bearer \u003cPlace Access Token Here\u003e", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/helper.BaseResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/helper.BaseResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/helper.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/helper.BaseResponse" + } + } + } } }, "/upload/avatar": { diff --git a/user/docs/swagger.json b/user/docs/swagger.json index b9a4d6a..76f8935 100644 --- a/user/docs/swagger.json +++ b/user/docs/swagger.json @@ -293,6 +293,60 @@ } } } + }, + "delete": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "User" + ], + "summary": "Delete Users Short URL", + "parameters": [ + { + "type": "string", + "description": "id short urls", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Authorization Bearer \u003cPlace Access Token Here\u003e", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/helper.BaseResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/helper.BaseResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/helper.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/helper.BaseResponse" + } + } + } } }, "/upload/avatar": { diff --git a/user/docs/swagger.yaml b/user/docs/swagger.yaml index 4322d50..506862b 100644 --- a/user/docs/swagger.yaml +++ b/user/docs/swagger.yaml @@ -144,6 +144,42 @@ paths: tags: - User /short/{id}: + delete: + consumes: + - application/json + parameters: + - description: id short urls + in: path + name: id + required: true + type: string + - description: Authorization Bearer + in: header + name: Authorization + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/helper.BaseResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/helper.BaseResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/helper.BaseResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/helper.BaseResponse' + summary: Delete Users Short URL + tags: + - User put: consumes: - application/json diff --git a/user/internal/v1/application/app.go b/user/internal/v1/application/app.go index 23506b8..db6a888 100644 --- a/user/internal/v1/application/app.go +++ b/user/internal/v1/application/app.go @@ -80,7 +80,7 @@ func SetupApplication(ctx context.Context) (*App, error) { return app, err } - queues := []string{app.Config.RabbitMQ.QueueCreateShortener, app.Config.RabbitMQ.QueueUpdateVisitor} + queues := []string{app.Config.RabbitMQ.QueueCreateShortener, app.Config.RabbitMQ.QueueUpdateVisitor, app.Config.RabbitMQ.QueueUpdateShortener, app.Config.RabbitMQ.QueueDeleteShortener} for _, q := range queues { _, err = amqpClient.QueueDeclare( diff --git a/user/internal/v1/config/config.go b/user/internal/v1/config/config.go index d8b9109..94cd23a 100644 --- a/user/internal/v1/config/config.go +++ b/user/internal/v1/config/config.go @@ -39,6 +39,7 @@ type ( QueueUpdateVisitor string QueueUploadAvatar string QueueUpdateShortener string + QueueDeleteShortener string } Secret struct { @@ -84,6 +85,7 @@ func loadConfiguration() *Configuration { QueueUpdateVisitor: helper.GetEnvString("AMQP_QUEUE_UPDATE_VISITOR"), QueueUploadAvatar: helper.GetEnvString("AMQP_QUEUE_UPLOAD_AVATAR"), QueueUpdateShortener: helper.GetEnvString("AMQP_QUEUE_UPDATE_SHORTENER"), + QueueDeleteShortener: helper.GetEnvString("AMQP_QUEUE_DELETE_SHORTENER"), }, Secret: &Secret{ JWTSecret: helper.GetEnvString("JWT_SECRET"), diff --git a/user/internal/v1/controller/user.go b/user/internal/v1/controller/user.go index 64a7980..75f1cbe 100644 --- a/user/internal/v1/controller/user.go +++ b/user/internal/v1/controller/user.go @@ -23,6 +23,7 @@ type ( EditProfile(ctx *fiber.Ctx) error UploadAvatar(ctx *fiber.Ctx) error UpdateShort(ctx *fiber.Ctx) error + DeleteShort(ctx *fiber.Ctx) error } // UserControllerImpl is an app user struct that consists of all the dependencies needed for user controller @@ -242,10 +243,40 @@ func (uc *UserControllerImpl) UpdateShort(ctx *fiber.Ctx) error { return helper.NewResponses[any](ctx, fiber.StatusBadRequest, "id required", model.NewError(model.Validation, "ID Required"), nil, nil) } - newShort, err := uc.UserSvc.UpdateUserShorts(shortID, &req) + _, err := uc.UserSvc.UpdateUserShorts(shortID, &req) if err != nil { return helper.NewResponses[any](ctx, fiber.StatusInternalServerError, err.Error(), nil, err, nil) } - return helper.NewResponses[any](ctx, fiber.StatusOK, "Success update Short URL's", newShort, nil, nil) + return helper.NewResponses[any](ctx, fiber.StatusOK, "Success update Short URL's", nil, nil, nil) +} + +// Check godoc +// @Summary Delete Users Short URL +// @Tags User +// @Accept json +// @Produce json +// @Param id path string true "id short urls" +// @Param Authorization header string true "Authorization Bearer " +// @Success 200 {object} helper.BaseResponse +// @Failure 400 {object} helper.BaseResponse +// @Failure 404 {object} helper.BaseResponse +// @Failure 500 {object} helper.BaseResponse +// @Router /short/{id} [delete] +func (uc *UserControllerImpl) DeleteShort(ctx *fiber.Ctx) error { + tr := uc.Tracer.Tracer("User-DeleteShort Controller") + _, span := tr.Start(uc.Context, "Start DeleteShort") + defer span.End() + + shortID := ctx.Params("id", "") + if shortID == "" { + return helper.NewResponses[any](ctx, fiber.StatusBadRequest, "id required", model.NewError(model.Validation, "ID Required"), nil, nil) + } + + _, err := uc.UserSvc.DeleteUserShorts(shortID) + if err != nil { + return helper.NewResponses[any](ctx, fiber.StatusInternalServerError, err.Error(), nil, err, nil) + } + + return helper.NewResponses[any](ctx, fiber.StatusOK, "Success delete Short URL's", nil, nil, nil) } diff --git a/user/internal/v1/infrastructure/http.go b/user/internal/v1/infrastructure/http.go index e2ec849..ba564f9 100644 --- a/user/internal/v1/infrastructure/http.go +++ b/user/internal/v1/infrastructure/http.go @@ -38,6 +38,8 @@ func setupRouter(app *application.App) { v1.Post("/upload/avatar", middleware.ValidateJWTMiddleware, dep.UserController.UploadAvatar) v1.Put("/short/:id", middleware.ValidateJWTMiddleware, dep.UserController.UpdateShort) + + v1.Delete("/short/:id", middleware.ValidateJWTMiddleware, dep.UserController.DeleteShort) } // handler for route not found diff --git a/user/internal/v1/repository/user.go b/user/internal/v1/repository/user.go index 4cbcb82..71fa01e 100644 --- a/user/internal/v1/repository/user.go +++ b/user/internal/v1/repository/user.go @@ -25,6 +25,7 @@ type ( PublishUploadAvatarUser(ctx context.Context, req *model.UploadAvatarRequest) error UpdateAvatarUserByID(ctx context.Context, fileURL string, userID string) error PublishUpdateUserShortener(ctx context.Context, shortID string, req *model.ShortUserRequest) error + PublishDeleteUserShortener(ctx context.Context, shortID string) error } // UserRepositoryImpl is an app user struct that consists of all the dependencies needed for user repository @@ -230,6 +231,44 @@ func (ur *UserRepositoryImpl) PublishUpdateUserShortener(ctx context.Context, sh return nil } +func (ur *UserRepositoryImpl) PublishDeleteUserShortener(ctx context.Context, shortID string) error { + tr := ur.Tracer.Tracer("User-PublishDeleteUserShortener Repository") + _, span := tr.Start(ctx, "Start PublishDeleteUserShortener") + defer span.End() + + ur.Logger.Info("data req before publish", shortID) + + // transform data to proto + msg := ur.prepareProtoPublishDeleteUserShortenerMessage(shortID) + + b, err := proto.Marshal(msg) + if err != nil { + ur.Logger.Error("UserRepositoryImpl.PublishDeleteUserShortener Marshal proto DeleteShortenerMessage ERROR, ", err) + return err + } + + message := amqp.Publishing{ + ContentType: "text/plain", + Body: []byte(b), + } + + // Attempt to publish a message to the queue. + if err := ur.RabbitMQ.Publish( + "", // exchange + ur.Config.RabbitMQ.QueueDeleteShortener, // queue name + false, // mandatory + false, // immediate + message, // message to publish + ); err != nil { + ur.Logger.Error("UserRepositoryImpl.PublishDeleteUserShortener RabbitMQ.Publish ERROR, ", err) + return err + } + + ur.Logger.Info("Success Publish User Shortener to Queue: ", ur.Config.RabbitMQ.QueueDeleteShortener) + + return nil +} + func (ur *UserRepositoryImpl) prepareProtoPublishCreateUserShortenerMessage(req *model.GenerateShortUserMessage) *shortenerpb.CreateShortenerMessage { return &shortenerpb.CreateShortenerMessage{ FullUrl: req.FullURL, @@ -252,3 +291,9 @@ func (ur *UserRepositoryImpl) prepareProtoPublishUpdateUserShortenerMessage(shor FullUrl: req.FullURL, } } + +func (ur *UserRepositoryImpl) prepareProtoPublishDeleteUserShortenerMessage(shortID string) *shortenerpb.DeleteShortenerMessage { + return &shortenerpb.DeleteShortenerMessage{ + Id: shortID, + } +} diff --git a/user/internal/v1/service/user.go b/user/internal/v1/service/user.go index 213134f..d7887fb 100644 --- a/user/internal/v1/service/user.go +++ b/user/internal/v1/service/user.go @@ -26,6 +26,7 @@ type ( UpdateUserProfile(userID string, req *model.EditProfileRequest) error UploadUserAvatar(ctx *fiber.Ctx, userID string) (*model.UploadAvatarResponse, error) UpdateUserShorts(shortID string, req *model.ShortUserRequest) (*model.ShortUserResponse, error) + DeleteUserShorts(shortID string) (*model.ShortUserResponse, error) } // UserServiceImpl is an app user struct that consists of all the dependencies needed for user service @@ -201,3 +202,16 @@ func (us *UserServiceImpl) UpdateUserShorts(shortID string, req *model.ShortUser return &model.ShortUserResponse{}, nil } + +func (us *UserServiceImpl) DeleteUserShorts(shortID string) (*model.ShortUserResponse, error) { + tr := us.Tracer.Tracer("User-DeleteUserShorts Service") + _, span := tr.Start(us.Context, "Start DeleteUserShorts") + defer span.End() + + err := us.UserRepo.PublishDeleteUserShortener(us.Context, shortID) + if err != nil { + return nil, err + } + + return &model.ShortUserResponse{}, nil +} diff --git a/user/pkg/api/v1/proto/shortener/shortener.pb.go b/user/pkg/api/v1/proto/shortener/shortener.pb.go index d5ad5e1..a4cf45b 100644 --- a/user/pkg/api/v1/proto/shortener/shortener.pb.go +++ b/user/pkg/api/v1/proto/shortener/shortener.pb.go @@ -350,6 +350,53 @@ func (x *UpdateShortenerMessage) GetFullUrl() string { return "" } +type DeleteShortenerMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *DeleteShortenerMessage) Reset() { + *x = DeleteShortenerMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_proto_shortener_shortener_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteShortenerMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteShortenerMessage) ProtoMessage() {} + +func (x *DeleteShortenerMessage) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_proto_shortener_shortener_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteShortenerMessage.ProtoReflect.Descriptor instead. +func (*DeleteShortenerMessage) Descriptor() ([]byte, []int) { + return file_api_v1_proto_shortener_shortener_proto_rawDescGZIP(), []int{6} +} + +func (x *DeleteShortenerMessage) GetId() string { + if x != nil { + return x.Id + } + return "" +} + var File_api_v1_proto_shortener_shortener_proto protoreflect.FileDescriptor var file_api_v1_proto_shortener_shortener_proto_rawDesc = []byte{ @@ -387,21 +434,24 @@ var file_api_v1_proto_shortener_shortener_proto_rawDesc = []byte{ 0x65, 0x6e, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x66, - 0x75, 0x6c, 0x6c, 0x55, 0x72, 0x6c, 0x32, 0x8b, 0x01, 0x0a, 0x10, 0x53, 0x68, 0x6f, 0x72, 0x74, - 0x65, 0x6e, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x77, 0x0a, 0x18, 0x47, - 0x65, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x42, - 0x79, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x2c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x50, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x50, 0x69, 0x63, 0x6b, 0x48, 0x44, 0x2f, 0x73, 0x69, 0x6e, 0x67, 0x6b, 0x61, - 0x74, 0x69, 0x6e, 0x2d, 0x72, 0x65, 0x76, 0x61, 0x6d, 0x70, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2f, - 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2f, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x3b, 0x73, 0x68, 0x6f, 0x72, 0x74, - 0x65, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x75, 0x6c, 0x6c, 0x55, 0x72, 0x6c, 0x22, 0x28, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x53, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x32, 0x8b, 0x01, 0x0a, 0x10, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x77, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x73, 0x74, + 0x53, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x42, 0x79, 0x55, 0x73, 0x65, 0x72, 0x49, + 0x44, 0x12, 0x2c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, + 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x73, + 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x68, 0x6f, + 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x50, + 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x50, 0x69, 0x63, + 0x6b, 0x48, 0x44, 0x2f, 0x73, 0x69, 0x6e, 0x67, 0x6b, 0x61, 0x74, 0x69, 0x6e, 0x2d, 0x72, 0x65, + 0x76, 0x61, 0x6d, 0x70, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x68, 0x6f, 0x72, 0x74, + 0x65, 0x6e, 0x65, 0x72, 0x3b, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x70, 0x62, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -416,7 +466,7 @@ func file_api_v1_proto_shortener_shortener_proto_rawDescGZIP() []byte { return file_api_v1_proto_shortener_shortener_proto_rawDescData } -var file_api_v1_proto_shortener_shortener_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_api_v1_proto_shortener_shortener_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_api_v1_proto_shortener_shortener_proto_goTypes = []interface{}{ (*Shortener)(nil), // 0: api.v1.proto.shortener.Shortener (*ListShortenerRequest)(nil), // 1: api.v1.proto.shortener.ListShortenerRequest @@ -424,6 +474,7 @@ var file_api_v1_proto_shortener_shortener_proto_goTypes = []interface{}{ (*CreateShortenerMessage)(nil), // 3: api.v1.proto.shortener.CreateShortenerMessage (*UpdateVisitorCountMessage)(nil), // 4: api.v1.proto.shortener.UpdateVisitorCountMessage (*UpdateShortenerMessage)(nil), // 5: api.v1.proto.shortener.UpdateShortenerMessage + (*DeleteShortenerMessage)(nil), // 6: api.v1.proto.shortener.DeleteShortenerMessage } var file_api_v1_proto_shortener_shortener_proto_depIdxs = []int32{ 0, // 0: api.v1.proto.shortener.ListShortenerResponse.shorteners:type_name -> api.v1.proto.shortener.Shortener @@ -514,6 +565,18 @@ func file_api_v1_proto_shortener_shortener_proto_init() { return nil } } + file_api_v1_proto_shortener_shortener_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteShortenerMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -521,7 +584,7 @@ func file_api_v1_proto_shortener_shortener_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_api_v1_proto_shortener_shortener_proto_rawDesc, NumEnums: 0, - NumMessages: 6, + NumMessages: 7, NumExtensions: 0, NumServices: 1, }, From 26d2f4d3c173077de1c1150cb62167389247e10a Mon Sep 17 00:00:00 2001 From: PickHD Date: Wed, 17 May 2023 16:36:55 +0700 Subject: [PATCH 2/2] feat (SRS-12) : update proto & implement consumer for delete users shortener --- .../api/v1/proto/shortener/shortener.proto | 4 + shortener/cmd/v1/example.env | 1 + shortener/cmd/v1/main.go | 2 +- shortener/internal/v1/application/app.go | 2 +- shortener/internal/v1/config/config.go | 2 + shortener/internal/v1/controller/short.go | 18 ++++ .../internal/v1/infrastructure/rabbitmq.go | 16 +++ shortener/internal/v1/model/short.go | 4 + shortener/internal/v1/repository/short.go | 22 +++++ shortener/internal/v1/service/short.go | 9 ++ .../api/v1/proto/shortener/shortener.pb.go | 98 +++++++++++++++---- 11 files changed, 158 insertions(+), 20 deletions(-) diff --git a/shortener/api/v1/proto/shortener/shortener.proto b/shortener/api/v1/proto/shortener/shortener.proto index f47b145..454cac1 100644 --- a/shortener/api/v1/proto/shortener/shortener.proto +++ b/shortener/api/v1/proto/shortener/shortener.proto @@ -38,3 +38,7 @@ message UpdateShortenerMessage { string full_url=2; } +message DeleteShortenerMessage { + string id = 1; +} + diff --git a/shortener/cmd/v1/example.env b/shortener/cmd/v1/example.env index ef3622f..61d90be 100644 --- a/shortener/cmd/v1/example.env +++ b/shortener/cmd/v1/example.env @@ -17,6 +17,7 @@ AMQP_SERVER_URL=amqp://guest:guest@amqp:5672/ AMQP_QUEUE_CREATE_SHORTENER=create-shortener-queue AMQP_QUEUE_UPDATE_VISITOR_COUNT=update-visitor-count-queue AMQP_QUEUE_UPDATE_SHORTENER=update-shortener-queue +AMQP_QUEUE_DELETE_SHORTENER=delete-shortener-queue GRPC_PORT=9091 diff --git a/shortener/cmd/v1/main.go b/shortener/cmd/v1/main.go index 76abeba..c6b556f 100644 --- a/shortener/cmd/v1/main.go +++ b/shortener/cmd/v1/main.go @@ -144,7 +144,7 @@ func main() { // Make a channel to receive messages into infinite loop. forever := make(chan bool) - queues := []string{app.Config.RabbitMQ.QueueCreateShortener, app.Config.RabbitMQ.QueueUpdateVisitor, app.Config.RabbitMQ.QueueUpdateShortener} + queues := []string{app.Config.RabbitMQ.QueueCreateShortener, app.Config.RabbitMQ.QueueUpdateVisitor, app.Config.RabbitMQ.QueueUpdateShortener, app.Config.RabbitMQ.QueueDeleteShortener} for _, q := range queues { go infrastructure.ConsumeMessages(app, q) diff --git a/shortener/internal/v1/application/app.go b/shortener/internal/v1/application/app.go index 956e187..49eb66a 100644 --- a/shortener/internal/v1/application/app.go +++ b/shortener/internal/v1/application/app.go @@ -89,7 +89,7 @@ func SetupApplication(ctx context.Context) (*App, error) { return app, err } - queues := []string{app.Config.RabbitMQ.QueueCreateShortener, app.Config.RabbitMQ.QueueUpdateVisitor, app.Config.RabbitMQ.QueueUpdateShortener} + queues := []string{app.Config.RabbitMQ.QueueCreateShortener, app.Config.RabbitMQ.QueueUpdateVisitor, app.Config.RabbitMQ.QueueUpdateShortener, app.Config.RabbitMQ.QueueDeleteShortener} for _, q := range queues { _, err = amqpClient.QueueDeclare( diff --git a/shortener/internal/v1/config/config.go b/shortener/internal/v1/config/config.go index b466d03..4625875 100644 --- a/shortener/internal/v1/config/config.go +++ b/shortener/internal/v1/config/config.go @@ -42,6 +42,7 @@ type ( QueueCreateShortener string QueueUpdateVisitor string QueueUpdateShortener string + QueueDeleteShortener string } Tracer struct { @@ -77,6 +78,7 @@ func loadConfiguration() *Configuration { QueueCreateShortener: helper.GetEnvString("AMQP_QUEUE_CREATE_SHORTENER"), QueueUpdateVisitor: helper.GetEnvString("AMQP_QUEUE_UPDATE_VISITOR_COUNT"), QueueUpdateShortener: helper.GetEnvString("AMQP_QUEUE_UPDATE_SHORTENER"), + QueueDeleteShortener: helper.GetEnvString("AMQP_QUEUE_DELETE_SHORTENER"), }, Tracer: &Tracer{ JaegerURL: helper.GetEnvString("JAEGER_URL"), diff --git a/shortener/internal/v1/controller/short.go b/shortener/internal/v1/controller/short.go index 09080e3..23d9ccb 100644 --- a/shortener/internal/v1/controller/short.go +++ b/shortener/internal/v1/controller/short.go @@ -30,6 +30,7 @@ type ( ProcessCreateShortUser(ctx context.Context, msg *shortenerpb.CreateShortenerMessage) error ProcessUpdateVisitorCount(ctx context.Context, msg *shortenerpb.UpdateVisitorCountMessage) error ProcessUpdateShortUser(ctx context.Context, msg *shortenerpb.UpdateShortenerMessage) error + ProcessDeleteShortUser(ctx context.Context, msg *shortenerpb.DeleteShortenerMessage) error } // ShortControllerImpl is an app short struct that consists of all the dependencies needed for short controller @@ -169,3 +170,20 @@ func (sc *ShortControllerImpl) ProcessUpdateShortUser(ctx context.Context, msg * return nil } + +func (sc *ShortControllerImpl) ProcessDeleteShortUser(ctx context.Context, msg *shortenerpb.DeleteShortenerMessage) error { + tr := sc.Tracer.Tracer("Shortener-ProcessDeleteShortUser Controller") + _, span := tr.Start(sc.Context, "Start ProcessDeleteShortUser") + defer span.End() + + req := &model.DeleteShortRequest{ + ID: msg.GetId(), + } + + err := sc.ShortSvc.DeleteShort(ctx, req) + if err != nil { + return model.NewError(model.Internal, err.Error()) + } + + return nil +} diff --git a/shortener/internal/v1/infrastructure/rabbitmq.go b/shortener/internal/v1/infrastructure/rabbitmq.go index 668ec38..2798827 100644 --- a/shortener/internal/v1/infrastructure/rabbitmq.go +++ b/shortener/internal/v1/infrastructure/rabbitmq.go @@ -78,6 +78,22 @@ func ConsumeMessages(app *application.App, queueName string) { app.Logger.Error("ProcessUpdateShortUser ERROR, ", err) } + app.Logger.Info(fmt.Sprintf("[%s] Success Process Message :", queueName), req) + case app.Config.RabbitMQ.QueueDeleteShortener: + req := &shortenerpb.DeleteShortenerMessage{} + + err := proto.Unmarshal(msg.Body, req) + if err != nil { + app.Logger.Error("Unmarshal proto DeleteShortenerMessage ERROR, ", err) + } + + app.Logger.Info(fmt.Sprintf("[%s] Success Consume Message :", queueName), req) + + err = dep.ShortController.ProcessDeleteShortUser(app.Context, req) + if err != nil { + app.Logger.Error("ProcessDeleteShortUser ERROR, ", err) + } + app.Logger.Info(fmt.Sprintf("[%s] Success Process Message :", queueName), req) } } diff --git a/shortener/internal/v1/model/short.go b/shortener/internal/v1/model/short.go index 1adc8b3..71fad21 100644 --- a/shortener/internal/v1/model/short.go +++ b/shortener/internal/v1/model/short.go @@ -35,4 +35,8 @@ type ( ID string `json:"id"` FullURL string `json:"full_url"` } + + DeleteShortRequest struct { + ID string `json:"id"` + } ) diff --git a/shortener/internal/v1/repository/short.go b/shortener/internal/v1/repository/short.go index 7ef7f08..a13d52e 100644 --- a/shortener/internal/v1/repository/short.go +++ b/shortener/internal/v1/repository/short.go @@ -30,6 +30,7 @@ type ( PublishUpdateVisitorCount(ctx context.Context, req *model.UpdateVisitorRequest) error UpdateVisitorByShortURL(ctx context.Context, req *model.UpdateVisitorRequest, lastVisitedCount int64) error UpdateFullURLByID(ctx context.Context, req *model.UpdateShortRequest) error + DeleteByID(ctx context.Context, req *model.DeleteShortRequest) error } // ShortRepositoryImpl is an app short struct that consists of all the dependencies needed for short repository @@ -237,6 +238,27 @@ func (sr *ShortRepositoryImpl) UpdateFullURLByID(ctx context.Context, req *model return nil } +func (sr *ShortRepositoryImpl) DeleteByID(ctx context.Context, req *model.DeleteShortRequest) error { + tr := sr.Tracer.Tracer("Shortener-DeleteByID Repository") + ctx, span := tr.Start(ctx, "Start DeleteByID") + defer span.End() + + objShortID, err := primitive.ObjectIDFromHex(req.ID) + if err != nil { + sr.Logger.Error("ShortRepositoryImpl.DeleteByID primitive.ObjectIDFromHex ERROR, ", err) + return err + } + + _, err = sr.DB.Collection(sr.Config.Database.ShortenersCollection).DeleteOne(ctx, + bson.D{{Key: "_id", Value: objShortID}}) + if err != nil { + sr.Logger.Error("ShortRepositoryImpl.DeleteByID DeleteOne ERROR, ", err) + return err + } + + return nil +} + func (sr *ShortRepositoryImpl) prepareProtoPublishUpdateVisitorCountMessage(req *model.UpdateVisitorRequest) *shortenerpb.UpdateVisitorCountMessage { return &shortenerpb.UpdateVisitorCountMessage{ ShortUrl: req.ShortURL, diff --git a/shortener/internal/v1/service/short.go b/shortener/internal/v1/service/short.go index 39b5ac7..85823cb 100644 --- a/shortener/internal/v1/service/short.go +++ b/shortener/internal/v1/service/short.go @@ -21,6 +21,7 @@ type ( ClickShort(shortURL string) (*model.ClickShortResponse, error) UpdateVisitorShort(ctx context.Context, req *model.UpdateVisitorRequest) error UpdateShort(ctx context.Context, req *model.UpdateShortRequest) error + DeleteShort(ctx context.Context, req *model.DeleteShortRequest) error } // ShortServiceImpl is an app short struct that consists of all the dependencies needed for short repository @@ -156,6 +157,14 @@ func (ss *ShortServiceImpl) UpdateShort(ctx context.Context, req *model.UpdateSh return ss.ShortRepo.UpdateFullURLByID(ctx, req) } +func (ss *ShortServiceImpl) DeleteShort(ctx context.Context, req *model.DeleteShortRequest) error { + tr := ss.Tracer.Tracer("Shortener-DeleteShort Service") + ctx, span := tr.Start(ctx, "Start DeleteShort") + defer span.End() + + return ss.ShortRepo.DeleteByID(ctx, req) +} + func (ss *ShortServiceImpl) validateCreateShort(req *model.CreateShortRequest) error { if _, err := url.ParseRequestURI(req.FullURL); err != nil { return model.NewError(model.Validation, err.Error()) diff --git a/shortener/pkg/api/v1/proto/shortener/shortener.pb.go b/shortener/pkg/api/v1/proto/shortener/shortener.pb.go index 56ac910..4f4c19b 100644 --- a/shortener/pkg/api/v1/proto/shortener/shortener.pb.go +++ b/shortener/pkg/api/v1/proto/shortener/shortener.pb.go @@ -350,6 +350,53 @@ func (x *UpdateShortenerMessage) GetFullUrl() string { return "" } +type DeleteShortenerMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *DeleteShortenerMessage) Reset() { + *x = DeleteShortenerMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_proto_shortener_shortener_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteShortenerMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteShortenerMessage) ProtoMessage() {} + +func (x *DeleteShortenerMessage) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_proto_shortener_shortener_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteShortenerMessage.ProtoReflect.Descriptor instead. +func (*DeleteShortenerMessage) Descriptor() ([]byte, []int) { + return file_api_v1_proto_shortener_shortener_proto_rawDescGZIP(), []int{6} +} + +func (x *DeleteShortenerMessage) GetId() string { + if x != nil { + return x.Id + } + return "" +} + var File_api_v1_proto_shortener_shortener_proto protoreflect.FileDescriptor var file_api_v1_proto_shortener_shortener_proto_rawDesc = []byte{ @@ -387,22 +434,24 @@ var file_api_v1_proto_shortener_shortener_proto_rawDesc = []byte{ 0x65, 0x6e, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x66, - 0x75, 0x6c, 0x6c, 0x55, 0x72, 0x6c, 0x32, 0x8b, 0x01, 0x0a, 0x10, 0x53, 0x68, 0x6f, 0x72, 0x74, - 0x65, 0x6e, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x77, 0x0a, 0x18, 0x47, - 0x65, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x42, - 0x79, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x2c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x55, 0x5a, 0x53, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x50, 0x69, 0x63, 0x6b, 0x48, 0x44, 0x2f, 0x73, 0x69, 0x6e, 0x67, 0x6b, 0x61, - 0x74, 0x69, 0x6e, 0x2d, 0x72, 0x65, 0x76, 0x61, 0x6d, 0x70, 0x2f, 0x73, 0x68, 0x6f, 0x72, 0x74, - 0x65, 0x6e, 0x65, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x3b, - 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x75, 0x6c, 0x6c, 0x55, 0x72, 0x6c, 0x22, 0x28, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x53, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x32, 0x8b, 0x01, 0x0a, 0x10, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x77, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x73, 0x74, + 0x53, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x42, 0x79, 0x55, 0x73, 0x65, 0x72, 0x49, + 0x44, 0x12, 0x2c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, + 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x73, + 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x68, 0x6f, + 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x55, + 0x5a, 0x53, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x50, 0x69, 0x63, + 0x6b, 0x48, 0x44, 0x2f, 0x73, 0x69, 0x6e, 0x67, 0x6b, 0x61, 0x74, 0x69, 0x6e, 0x2d, 0x72, 0x65, + 0x76, 0x61, 0x6d, 0x70, 0x2f, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x2f, 0x70, + 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x3b, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, + 0x6e, 0x65, 0x72, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -417,7 +466,7 @@ func file_api_v1_proto_shortener_shortener_proto_rawDescGZIP() []byte { return file_api_v1_proto_shortener_shortener_proto_rawDescData } -var file_api_v1_proto_shortener_shortener_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_api_v1_proto_shortener_shortener_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_api_v1_proto_shortener_shortener_proto_goTypes = []interface{}{ (*Shortener)(nil), // 0: api.v1.proto.shortener.Shortener (*ListShortenerRequest)(nil), // 1: api.v1.proto.shortener.ListShortenerRequest @@ -425,6 +474,7 @@ var file_api_v1_proto_shortener_shortener_proto_goTypes = []interface{}{ (*CreateShortenerMessage)(nil), // 3: api.v1.proto.shortener.CreateShortenerMessage (*UpdateVisitorCountMessage)(nil), // 4: api.v1.proto.shortener.UpdateVisitorCountMessage (*UpdateShortenerMessage)(nil), // 5: api.v1.proto.shortener.UpdateShortenerMessage + (*DeleteShortenerMessage)(nil), // 6: api.v1.proto.shortener.DeleteShortenerMessage } var file_api_v1_proto_shortener_shortener_proto_depIdxs = []int32{ 0, // 0: api.v1.proto.shortener.ListShortenerResponse.shorteners:type_name -> api.v1.proto.shortener.Shortener @@ -515,6 +565,18 @@ func file_api_v1_proto_shortener_shortener_proto_init() { return nil } } + file_api_v1_proto_shortener_shortener_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteShortenerMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -522,7 +584,7 @@ func file_api_v1_proto_shortener_shortener_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_api_v1_proto_shortener_shortener_proto_rawDesc, NumEnums: 0, - NumMessages: 6, + NumMessages: 7, NumExtensions: 0, NumServices: 1, },