refactor: webhook service

pull/4785/head
johnnyjoy 4 months ago
parent 03399a6007
commit d6a75bba4c

@ -9,7 +9,6 @@ import (
"time"
"github.com/pkg/errors"
"google.golang.org/protobuf/encoding/protojson"
v1pb "github.com/usememos/memos/proto/gen/api/v1"
)
@ -19,9 +18,20 @@ var (
timeout = 30 * time.Second
)
type WebhookRequestPayload struct {
// The target URL for the webhook request.
Url string `json:"url"`
// The type of activity that triggered this webhook.
ActivityType string `json:"activityType"`
// The resource name of the creator. Format: users/{user}
Creator string `json:"creator"`
// The memo that triggered this webhook (if applicable).
Memo *v1pb.Memo `json:"memo"`
}
// Post posts the message to webhook endpoint.
func Post(requestPayload *v1pb.WebhookRequestPayload) error {
body, err := protojson.Marshal(requestPayload)
func Post(requestPayload *WebhookRequestPayload) error {
body, err := json.Marshal(requestPayload)
if err != nil {
return errors.Wrapf(err, "failed to marshal webhook request to %s", requestPayload.Url)
}
@ -67,7 +77,7 @@ func Post(requestPayload *v1pb.WebhookRequestPayload) error {
// PostAsync posts the message to webhook endpoint asynchronously.
// It spawns a new goroutine to handle the request and does not wait for the response.
func PostAsync(requestPayload *v1pb.WebhookRequestPayload) {
func PostAsync(requestPayload *WebhookRequestPayload) {
go func() {
if err := Post(requestPayload); err != nil {
// Since we're in a goroutine, we can only log the error

@ -2,7 +2,6 @@ syntax = "proto3";
package memos.api.v1;
import "api/v1/common.proto";
import "api/v1/memo_service.proto";
import "google/api/annotations.proto";
import "google/api/client.proto";
@ -10,43 +9,43 @@ import "google/api/field_behavior.proto";
import "google/api/resource.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/field_mask.proto";
import "google/protobuf/timestamp.proto";
option go_package = "gen/api/v1";
service WebhookService {
// ListWebhooks returns a list of webhooks.
// ListWebhooks returns a list of webhooks for a user.
rpc ListWebhooks(ListWebhooksRequest) returns (ListWebhooksResponse) {
option (google.api.http) = {get: "/api/v1/webhooks"};
option (google.api.http) = {get: "/api/v1/{parent=users/*}/webhooks"};
option (google.api.method_signature) = "parent";
}
// GetWebhook gets a webhook by name.
rpc GetWebhook(GetWebhookRequest) returns (Webhook) {
option (google.api.http) = {get: "/api/v1/{name=webhooks/*}"};
option (google.api.http) = {get: "/api/v1/{name=users/*/webhooks/*}"};
option (google.api.method_signature) = "name";
}
// CreateWebhook creates a new webhook.
// CreateWebhook creates a new webhook for a user.
rpc CreateWebhook(CreateWebhookRequest) returns (Webhook) {
option (google.api.http) = {
post: "/api/v1/webhooks"
post: "/api/v1/{parent=users/*}/webhooks"
body: "webhook"
};
option (google.api.method_signature) = "webhook";
option (google.api.method_signature) = "parent,webhook";
}
// UpdateWebhook updates a webhook.
// UpdateWebhook updates a webhook for a user.
rpc UpdateWebhook(UpdateWebhookRequest) returns (Webhook) {
option (google.api.http) = {
patch: "/api/v1/{webhook.name=webhooks/*}"
patch: "/api/v1/{webhook.name=users/*/webhooks/*}"
body: "webhook"
};
option (google.api.method_signature) = "webhook,update_mask";
}
// DeleteWebhook deletes a webhook.
// DeleteWebhook deletes a webhook for a user.
rpc DeleteWebhook(DeleteWebhookRequest) returns (google.protobuf.Empty) {
option (google.api.http) = {delete: "/api/v1/{name=webhooks/*}"};
option (google.api.http) = {delete: "/api/v1/{name=users/*/webhooks/*}"};
option (google.api.method_signature) = "name";
}
}
@ -54,46 +53,39 @@ service WebhookService {
message Webhook {
option (google.api.resource) = {
type: "memos.api.v1/Webhook"
pattern: "webhooks/{webhook}"
name_field: "name"
pattern: "users/{user}/webhooks/{webhook}"
singular: "webhook"
plural: "webhooks"
};
// The resource name of the webhook.
// Format: webhooks/{webhook}
// Format: users/{user}/webhooks/{webhook}
string name = 1 [(google.api.field_behavior) = IDENTIFIER];
// Required. The display name of the webhook.
// The display name of the webhook.
string display_name = 2 [(google.api.field_behavior) = REQUIRED];
// Required. The target URL for the webhook.
// The target URL for the webhook.
string url = 3 [(google.api.field_behavior) = REQUIRED];
}
// Output only. The resource name of the creator.
message ListWebhooksRequest {
// Required. The parent resource where webhooks are listed.
// Format: users/{user}
string creator = 4 [(google.api.field_behavior) = OUTPUT_ONLY];
// The state of the webhook.
State state = 5 [(google.api.field_behavior) = REQUIRED];
// Output only. The creation timestamp.
google.protobuf.Timestamp create_time = 6 [(google.api.field_behavior) = OUTPUT_ONLY];
// Output only. The last update timestamp.
google.protobuf.Timestamp update_time = 7 [(google.api.field_behavior) = OUTPUT_ONLY];
string parent = 1 [
(google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {child_type: "memos.api.v1/Webhook"}
];
}
message ListWebhooksRequest {}
message ListWebhooksResponse {
// The list of webhooks.
repeated Webhook webhooks = 1;
}
message GetWebhookRequest {
// Required. The resource name of the webhook.
// Format: webhooks/{webhook}
// Required. The resource name of the webhook to retrieve.
// Format: users/{user}/webhooks/{webhook}
string name = 1 [
(google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {type: "memos.api.v1/Webhook"}
@ -101,55 +93,33 @@ message GetWebhookRequest {
}
message CreateWebhookRequest {
// Required. The webhook to create.
Webhook webhook = 1 [
// Required. The parent resource where this webhook will be created.
// Format: users/{user}
string parent = 1 [
(google.api.field_behavior) = REQUIRED,
(google.api.field_behavior) = INPUT_ONLY
(google.api.resource_reference) = {child_type: "memos.api.v1/Webhook"}
];
// Optional. The webhook ID to use for this webhook.
// If empty, a unique ID will be generated.
// Must match the pattern [a-z0-9-]+
string webhook_id = 2 [(google.api.field_behavior) = OPTIONAL];
// Required. The webhook to create.
Webhook webhook = 2 [(google.api.field_behavior) = REQUIRED];
// Optional. If set, validate the request but don't actually create the webhook.
// Optional. If set, validate the request, but do not actually create the webhook.
bool validate_only = 3 [(google.api.field_behavior) = OPTIONAL];
}
message UpdateWebhookRequest {
// Required. The webhook to update.
// Required. The webhook resource which replaces the resource on the server.
Webhook webhook = 1 [(google.api.field_behavior) = REQUIRED];
// Required. The list of fields to update.
google.protobuf.FieldMask update_mask = 2 [(google.api.field_behavior) = REQUIRED];
// Optional. The list of fields to update.
google.protobuf.FieldMask update_mask = 2 [(google.api.field_behavior) = OPTIONAL];
}
message DeleteWebhookRequest {
// Required. The resource name of the webhook to delete.
// Format: webhooks/{webhook}
// Format: users/{user}/webhooks/{webhook}
string name = 1 [
(google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {type: "memos.api.v1/Webhook"}
];
}
message WebhookRequestPayload {
// The target URL for the webhook request.
string url = 1 [(google.api.field_behavior) = REQUIRED];
// The type of activity that triggered this webhook.
string activity_type = 2 [(google.api.field_behavior) = REQUIRED];
// The resource name of the creator.
// Format: users/{user}
string creator = 3 [
(google.api.field_behavior) = OUTPUT_ONLY,
(google.api.resource_reference) = {type: "memos.api.v1/User"}
];
// The creation timestamp of the activity.
google.protobuf.Timestamp create_time = 4 [(google.api.field_behavior) = OUTPUT_ONLY];
// The memo that triggered this webhook (if applicable).
Memo memo = 5 [(google.api.field_behavior) = OPTIONAL];
}

@ -12,7 +12,6 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
emptypb "google.golang.org/protobuf/types/known/emptypb"
fieldmaskpb "google.golang.org/protobuf/types/known/fieldmaskpb"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
reflect "reflect"
sync "sync"
unsafe "unsafe"
@ -28,21 +27,12 @@ const (
type Webhook struct {
state protoimpl.MessageState `protogen:"open.v1"`
// The resource name of the webhook.
// Format: webhooks/{webhook}
// Format: users/{user}/webhooks/{webhook}
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// Required. The display name of the webhook.
// The display name of the webhook.
DisplayName string `protobuf:"bytes,2,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"`
// Required. The target URL for the webhook.
Url string `protobuf:"bytes,3,opt,name=url,proto3" json:"url,omitempty"`
// Output only. The resource name of the creator.
// Format: users/{user}
Creator string `protobuf:"bytes,4,opt,name=creator,proto3" json:"creator,omitempty"`
// The state of the webhook.
State State `protobuf:"varint,5,opt,name=state,proto3,enum=memos.api.v1.State" json:"state,omitempty"`
// Output only. The creation timestamp.
CreateTime *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty"`
// Output only. The last update timestamp.
UpdateTime *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty"`
// The target URL for the webhook.
Url string `protobuf:"bytes,3,opt,name=url,proto3" json:"url,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@ -98,36 +88,11 @@ func (x *Webhook) GetUrl() string {
return ""
}
func (x *Webhook) GetCreator() string {
if x != nil {
return x.Creator
}
return ""
}
func (x *Webhook) GetState() State {
if x != nil {
return x.State
}
return State_STATE_UNSPECIFIED
}
func (x *Webhook) GetCreateTime() *timestamppb.Timestamp {
if x != nil {
return x.CreateTime
}
return nil
}
func (x *Webhook) GetUpdateTime() *timestamppb.Timestamp {
if x != nil {
return x.UpdateTime
}
return nil
}
type ListWebhooksRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
state protoimpl.MessageState `protogen:"open.v1"`
// Required. The parent resource where webhooks are listed.
// Format: users/{user}
Parent string `protobuf:"bytes,1,opt,name=parent,proto3" json:"parent,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@ -162,6 +127,13 @@ func (*ListWebhooksRequest) Descriptor() ([]byte, []int) {
return file_api_v1_webhook_service_proto_rawDescGZIP(), []int{1}
}
func (x *ListWebhooksRequest) GetParent() string {
if x != nil {
return x.Parent
}
return ""
}
type ListWebhooksResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
// The list of webhooks.
@ -209,8 +181,8 @@ func (x *ListWebhooksResponse) GetWebhooks() []*Webhook {
type GetWebhookRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Required. The resource name of the webhook.
// Format: webhooks/{webhook}
// Required. The resource name of the webhook to retrieve.
// Format: users/{user}/webhooks/{webhook}
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
@ -255,13 +227,12 @@ func (x *GetWebhookRequest) GetName() string {
type CreateWebhookRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Required. The parent resource where this webhook will be created.
// Format: users/{user}
Parent string `protobuf:"bytes,1,opt,name=parent,proto3" json:"parent,omitempty"`
// Required. The webhook to create.
Webhook *Webhook `protobuf:"bytes,1,opt,name=webhook,proto3" json:"webhook,omitempty"`
// Optional. The webhook ID to use for this webhook.
// If empty, a unique ID will be generated.
// Must match the pattern [a-z0-9-]+
WebhookId string `protobuf:"bytes,2,opt,name=webhook_id,json=webhookId,proto3" json:"webhook_id,omitempty"`
// Optional. If set, validate the request but don't actually create the webhook.
Webhook *Webhook `protobuf:"bytes,2,opt,name=webhook,proto3" json:"webhook,omitempty"`
// Optional. If set, validate the request, but do not actually create the webhook.
ValidateOnly bool `protobuf:"varint,3,opt,name=validate_only,json=validateOnly,proto3" json:"validate_only,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
@ -297,18 +268,18 @@ func (*CreateWebhookRequest) Descriptor() ([]byte, []int) {
return file_api_v1_webhook_service_proto_rawDescGZIP(), []int{4}
}
func (x *CreateWebhookRequest) GetWebhook() *Webhook {
func (x *CreateWebhookRequest) GetParent() string {
if x != nil {
return x.Webhook
return x.Parent
}
return nil
return ""
}
func (x *CreateWebhookRequest) GetWebhookId() string {
func (x *CreateWebhookRequest) GetWebhook() *Webhook {
if x != nil {
return x.WebhookId
return x.Webhook
}
return ""
return nil
}
func (x *CreateWebhookRequest) GetValidateOnly() bool {
@ -320,9 +291,9 @@ func (x *CreateWebhookRequest) GetValidateOnly() bool {
type UpdateWebhookRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Required. The webhook to update.
// Required. The webhook resource which replaces the resource on the server.
Webhook *Webhook `protobuf:"bytes,1,opt,name=webhook,proto3" json:"webhook,omitempty"`
// Required. The list of fields to update.
// Optional. The list of fields to update.
UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
@ -375,7 +346,7 @@ func (x *UpdateWebhookRequest) GetUpdateMask() *fieldmaskpb.FieldMask {
type DeleteWebhookRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Required. The resource name of the webhook to delete.
// Format: webhooks/{webhook}
// Format: users/{user}/webhooks/{webhook}
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
@ -418,137 +389,41 @@ func (x *DeleteWebhookRequest) GetName() string {
return ""
}
type WebhookRequestPayload struct {
state protoimpl.MessageState `protogen:"open.v1"`
// The target URL for the webhook request.
Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"`
// The type of activity that triggered this webhook.
ActivityType string `protobuf:"bytes,2,opt,name=activity_type,json=activityType,proto3" json:"activity_type,omitempty"`
// The resource name of the creator.
// Format: users/{user}
Creator string `protobuf:"bytes,3,opt,name=creator,proto3" json:"creator,omitempty"`
// The creation timestamp of the activity.
CreateTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty"`
// The memo that triggered this webhook (if applicable).
Memo *Memo `protobuf:"bytes,5,opt,name=memo,proto3" json:"memo,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *WebhookRequestPayload) Reset() {
*x = WebhookRequestPayload{}
mi := &file_api_v1_webhook_service_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *WebhookRequestPayload) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*WebhookRequestPayload) ProtoMessage() {}
func (x *WebhookRequestPayload) ProtoReflect() protoreflect.Message {
mi := &file_api_v1_webhook_service_proto_msgTypes[7]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use WebhookRequestPayload.ProtoReflect.Descriptor instead.
func (*WebhookRequestPayload) Descriptor() ([]byte, []int) {
return file_api_v1_webhook_service_proto_rawDescGZIP(), []int{7}
}
func (x *WebhookRequestPayload) GetUrl() string {
if x != nil {
return x.Url
}
return ""
}
func (x *WebhookRequestPayload) GetActivityType() string {
if x != nil {
return x.ActivityType
}
return ""
}
func (x *WebhookRequestPayload) GetCreator() string {
if x != nil {
return x.Creator
}
return ""
}
func (x *WebhookRequestPayload) GetCreateTime() *timestamppb.Timestamp {
if x != nil {
return x.CreateTime
}
return nil
}
func (x *WebhookRequestPayload) GetMemo() *Memo {
if x != nil {
return x.Memo
}
return nil
}
var File_api_v1_webhook_service_proto protoreflect.FileDescriptor
const file_api_v1_webhook_service_proto_rawDesc = "" +
"\n" +
"\x1capi/v1/webhook_service.proto\x12\fmemos.api.v1\x1a\x13api/v1/common.proto\x1a\x19api/v1/memo_service.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xfc\x02\n" +
"\x1capi/v1/webhook_service.proto\x12\fmemos.api.v1\x1a\x19api/v1/memo_service.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\"\xb0\x01\n" +
"\aWebhook\x12\x17\n" +
"\x04name\x18\x01 \x01(\tB\x03\xe0A\bR\x04name\x12&\n" +
"\fdisplay_name\x18\x02 \x01(\tB\x03\xe0A\x02R\vdisplayName\x12\x15\n" +
"\x03url\x18\x03 \x01(\tB\x03\xe0A\x02R\x03url\x12\x1d\n" +
"\acreator\x18\x04 \x01(\tB\x03\xe0A\x03R\acreator\x12.\n" +
"\x05state\x18\x05 \x01(\x0e2\x13.memos.api.v1.StateB\x03\xe0A\x02R\x05state\x12@\n" +
"\vcreate_time\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampB\x03\xe0A\x03R\n" +
"createTime\x12@\n" +
"\vupdate_time\x18\a \x01(\v2\x1a.google.protobuf.TimestampB\x03\xe0A\x03R\n" +
"updateTime:F\xeaAC\n" +
"\x14memos.api.v1/Webhook\x12\x12webhooks/{webhook}\x1a\x04name*\bwebhooks2\awebhook\"\x15\n" +
"\x13ListWebhooksRequest\"I\n" +
"\x03url\x18\x03 \x01(\tB\x03\xe0A\x02R\x03url:M\xeaAJ\n" +
"\x14memos.api.v1/Webhook\x12\x1fusers/{user}/webhooks/{webhook}*\bwebhooks2\awebhook\"K\n" +
"\x13ListWebhooksRequest\x124\n" +
"\x06parent\x18\x01 \x01(\tB\x1c\xe0A\x02\xfaA\x16\x12\x14memos.api.v1/WebhookR\x06parent\"I\n" +
"\x14ListWebhooksResponse\x121\n" +
"\bwebhooks\x18\x01 \x03(\v2\x15.memos.api.v1.WebhookR\bwebhooks\"E\n" +
"\x11GetWebhookRequest\x120\n" +
"\x04name\x18\x01 \x01(\tB\x1c\xe0A\x02\xfaA\x16\n" +
"\x14memos.api.v1/WebhookR\x04name\"\x9d\x01\n" +
"\x14CreateWebhookRequest\x127\n" +
"\awebhook\x18\x01 \x01(\v2\x15.memos.api.v1.WebhookB\x06\xe0A\x02\xe0A\x04R\awebhook\x12\"\n" +
"\n" +
"webhook_id\x18\x02 \x01(\tB\x03\xe0A\x01R\twebhookId\x12(\n" +
"\x14memos.api.v1/WebhookR\x04name\"\xac\x01\n" +
"\x14CreateWebhookRequest\x124\n" +
"\x06parent\x18\x01 \x01(\tB\x1c\xe0A\x02\xfaA\x16\x12\x14memos.api.v1/WebhookR\x06parent\x124\n" +
"\awebhook\x18\x02 \x01(\v2\x15.memos.api.v1.WebhookB\x03\xe0A\x02R\awebhook\x12(\n" +
"\rvalidate_only\x18\x03 \x01(\bB\x03\xe0A\x01R\fvalidateOnly\"\x8e\x01\n" +
"\x14UpdateWebhookRequest\x124\n" +
"\awebhook\x18\x01 \x01(\v2\x15.memos.api.v1.WebhookB\x03\xe0A\x02R\awebhook\x12@\n" +
"\vupdate_mask\x18\x02 \x01(\v2\x1a.google.protobuf.FieldMaskB\x03\xe0A\x02R\n" +
"\vupdate_mask\x18\x02 \x01(\v2\x1a.google.protobuf.FieldMaskB\x03\xe0A\x01R\n" +
"updateMask\"H\n" +
"\x14DeleteWebhookRequest\x120\n" +
"\x04name\x18\x01 \x01(\tB\x1c\xe0A\x02\xfaA\x16\n" +
"\x14memos.api.v1/WebhookR\x04name\"\xfc\x01\n" +
"\x15WebhookRequestPayload\x12\x15\n" +
"\x03url\x18\x01 \x01(\tB\x03\xe0A\x02R\x03url\x12(\n" +
"\ractivity_type\x18\x02 \x01(\tB\x03\xe0A\x02R\factivityType\x123\n" +
"\acreator\x18\x03 \x01(\tB\x19\xe0A\x03\xfaA\x13\n" +
"\x11memos.api.v1/UserR\acreator\x12@\n" +
"\vcreate_time\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampB\x03\xe0A\x03R\n" +
"createTime\x12+\n" +
"\x04memo\x18\x05 \x01(\v2\x12.memos.api.v1.MemoB\x03\xe0A\x01R\x04memo2\xf8\x04\n" +
"\x0eWebhookService\x12o\n" +
"\fListWebhooks\x12!.memos.api.v1.ListWebhooksRequest\x1a\".memos.api.v1.ListWebhooksResponse\"\x18\x82\xd3\xe4\x93\x02\x12\x12\x10/api/v1/webhooks\x12n\n" +
"\x14memos.api.v1/WebhookR\x04name2\xc4\x05\n" +
"\x0eWebhookService\x12\x89\x01\n" +
"\fListWebhooks\x12!.memos.api.v1.ListWebhooksRequest\x1a\".memos.api.v1.ListWebhooksResponse\"2\xdaA\x06parent\x82\xd3\xe4\x93\x02#\x12!/api/v1/{parent=users/*}/webhooks\x12v\n" +
"\n" +
"GetWebhook\x12\x1f.memos.api.v1.GetWebhookRequest\x1a\x15.memos.api.v1.Webhook\"(\xdaA\x04name\x82\xd3\xe4\x93\x02\x1b\x12\x19/api/v1/{name=webhooks/*}\x12w\n" +
"\rCreateWebhook\x12\".memos.api.v1.CreateWebhookRequest\x1a\x15.memos.api.v1.Webhook\"+\xdaA\awebhook\x82\xd3\xe4\x93\x02\x1b:\awebhook\"\x10/api/v1/webhooks\x12\x94\x01\n" +
"\rUpdateWebhook\x12\".memos.api.v1.UpdateWebhookRequest\x1a\x15.memos.api.v1.Webhook\"H\xdaA\x13webhook,update_mask\x82\xd3\xe4\x93\x02,:\awebhook2!/api/v1/{webhook.name=webhooks/*}\x12u\n" +
"\rDeleteWebhook\x12\".memos.api.v1.DeleteWebhookRequest\x1a\x16.google.protobuf.Empty\"(\xdaA\x04name\x82\xd3\xe4\x93\x02\x1b*\x19/api/v1/{name=webhooks/*}B\xab\x01\n" +
"GetWebhook\x12\x1f.memos.api.v1.GetWebhookRequest\x1a\x15.memos.api.v1.Webhook\"0\xdaA\x04name\x82\xd3\xe4\x93\x02#\x12!/api/v1/{name=users/*/webhooks/*}\x12\x8f\x01\n" +
"\rCreateWebhook\x12\".memos.api.v1.CreateWebhookRequest\x1a\x15.memos.api.v1.Webhook\"C\xdaA\x0eparent,webhook\x82\xd3\xe4\x93\x02,:\awebhook\"!/api/v1/{parent=users/*}/webhooks\x12\x9c\x01\n" +
"\rUpdateWebhook\x12\".memos.api.v1.UpdateWebhookRequest\x1a\x15.memos.api.v1.Webhook\"P\xdaA\x13webhook,update_mask\x82\xd3\xe4\x93\x024:\awebhook2)/api/v1/{webhook.name=users/*/webhooks/*}\x12}\n" +
"\rDeleteWebhook\x12\".memos.api.v1.DeleteWebhookRequest\x1a\x16.google.protobuf.Empty\"0\xdaA\x04name\x82\xd3\xe4\x93\x02#*!/api/v1/{name=users/*/webhooks/*}B\xab\x01\n" +
"\x10com.memos.api.v1B\x13WebhookServiceProtoP\x01Z0github.com/usememos/memos/proto/gen/api/v1;apiv1\xa2\x02\x03MAX\xaa\x02\fMemos.Api.V1\xca\x02\fMemos\\Api\\V1\xe2\x02\x18Memos\\Api\\V1\\GPBMetadata\xea\x02\x0eMemos::Api::V1b\x06proto3"
var (
@ -563,7 +438,7 @@ func file_api_v1_webhook_service_proto_rawDescGZIP() []byte {
return file_api_v1_webhook_service_proto_rawDescData
}
var file_api_v1_webhook_service_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
var file_api_v1_webhook_service_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_api_v1_webhook_service_proto_goTypes = []any{
(*Webhook)(nil), // 0: memos.api.v1.Webhook
(*ListWebhooksRequest)(nil), // 1: memos.api.v1.ListWebhooksRequest
@ -572,38 +447,29 @@ var file_api_v1_webhook_service_proto_goTypes = []any{
(*CreateWebhookRequest)(nil), // 4: memos.api.v1.CreateWebhookRequest
(*UpdateWebhookRequest)(nil), // 5: memos.api.v1.UpdateWebhookRequest
(*DeleteWebhookRequest)(nil), // 6: memos.api.v1.DeleteWebhookRequest
(*WebhookRequestPayload)(nil), // 7: memos.api.v1.WebhookRequestPayload
(State)(0), // 8: memos.api.v1.State
(*timestamppb.Timestamp)(nil), // 9: google.protobuf.Timestamp
(*fieldmaskpb.FieldMask)(nil), // 10: google.protobuf.FieldMask
(*Memo)(nil), // 11: memos.api.v1.Memo
(*emptypb.Empty)(nil), // 12: google.protobuf.Empty
(*fieldmaskpb.FieldMask)(nil), // 7: google.protobuf.FieldMask
(*emptypb.Empty)(nil), // 8: google.protobuf.Empty
}
var file_api_v1_webhook_service_proto_depIdxs = []int32{
8, // 0: memos.api.v1.Webhook.state:type_name -> memos.api.v1.State
9, // 1: memos.api.v1.Webhook.create_time:type_name -> google.protobuf.Timestamp
9, // 2: memos.api.v1.Webhook.update_time:type_name -> google.protobuf.Timestamp
0, // 3: memos.api.v1.ListWebhooksResponse.webhooks:type_name -> memos.api.v1.Webhook
0, // 4: memos.api.v1.CreateWebhookRequest.webhook:type_name -> memos.api.v1.Webhook
0, // 5: memos.api.v1.UpdateWebhookRequest.webhook:type_name -> memos.api.v1.Webhook
10, // 6: memos.api.v1.UpdateWebhookRequest.update_mask:type_name -> google.protobuf.FieldMask
9, // 7: memos.api.v1.WebhookRequestPayload.create_time:type_name -> google.protobuf.Timestamp
11, // 8: memos.api.v1.WebhookRequestPayload.memo:type_name -> memos.api.v1.Memo
1, // 9: memos.api.v1.WebhookService.ListWebhooks:input_type -> memos.api.v1.ListWebhooksRequest
3, // 10: memos.api.v1.WebhookService.GetWebhook:input_type -> memos.api.v1.GetWebhookRequest
4, // 11: memos.api.v1.WebhookService.CreateWebhook:input_type -> memos.api.v1.CreateWebhookRequest
5, // 12: memos.api.v1.WebhookService.UpdateWebhook:input_type -> memos.api.v1.UpdateWebhookRequest
6, // 13: memos.api.v1.WebhookService.DeleteWebhook:input_type -> memos.api.v1.DeleteWebhookRequest
2, // 14: memos.api.v1.WebhookService.ListWebhooks:output_type -> memos.api.v1.ListWebhooksResponse
0, // 15: memos.api.v1.WebhookService.GetWebhook:output_type -> memos.api.v1.Webhook
0, // 16: memos.api.v1.WebhookService.CreateWebhook:output_type -> memos.api.v1.Webhook
0, // 17: memos.api.v1.WebhookService.UpdateWebhook:output_type -> memos.api.v1.Webhook
12, // 18: memos.api.v1.WebhookService.DeleteWebhook:output_type -> google.protobuf.Empty
14, // [14:19] is the sub-list for method output_type
9, // [9:14] is the sub-list for method input_type
9, // [9:9] is the sub-list for extension type_name
9, // [9:9] is the sub-list for extension extendee
0, // [0:9] is the sub-list for field type_name
0, // 0: memos.api.v1.ListWebhooksResponse.webhooks:type_name -> memos.api.v1.Webhook
0, // 1: memos.api.v1.CreateWebhookRequest.webhook:type_name -> memos.api.v1.Webhook
0, // 2: memos.api.v1.UpdateWebhookRequest.webhook:type_name -> memos.api.v1.Webhook
7, // 3: memos.api.v1.UpdateWebhookRequest.update_mask:type_name -> google.protobuf.FieldMask
1, // 4: memos.api.v1.WebhookService.ListWebhooks:input_type -> memos.api.v1.ListWebhooksRequest
3, // 5: memos.api.v1.WebhookService.GetWebhook:input_type -> memos.api.v1.GetWebhookRequest
4, // 6: memos.api.v1.WebhookService.CreateWebhook:input_type -> memos.api.v1.CreateWebhookRequest
5, // 7: memos.api.v1.WebhookService.UpdateWebhook:input_type -> memos.api.v1.UpdateWebhookRequest
6, // 8: memos.api.v1.WebhookService.DeleteWebhook:input_type -> memos.api.v1.DeleteWebhookRequest
2, // 9: memos.api.v1.WebhookService.ListWebhooks:output_type -> memos.api.v1.ListWebhooksResponse
0, // 10: memos.api.v1.WebhookService.GetWebhook:output_type -> memos.api.v1.Webhook
0, // 11: memos.api.v1.WebhookService.CreateWebhook:output_type -> memos.api.v1.Webhook
0, // 12: memos.api.v1.WebhookService.UpdateWebhook:output_type -> memos.api.v1.Webhook
8, // 13: memos.api.v1.WebhookService.DeleteWebhook:output_type -> google.protobuf.Empty
9, // [9:14] is the sub-list for method output_type
4, // [4:9] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension type_name
4, // [4:4] is the sub-list for extension extendee
0, // [0:4] is the sub-list for field type_name
}
func init() { file_api_v1_webhook_service_proto_init() }
@ -611,7 +477,6 @@ func file_api_v1_webhook_service_proto_init() {
if File_api_v1_webhook_service_proto != nil {
return
}
file_api_v1_common_proto_init()
file_api_v1_memo_service_proto_init()
type x struct{}
out := protoimpl.TypeBuilder{
@ -619,7 +484,7 @@ func file_api_v1_webhook_service_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_v1_webhook_service_proto_rawDesc), len(file_api_v1_webhook_service_proto_rawDesc)),
NumEnums: 0,
NumMessages: 8,
NumMessages: 7,
NumExtensions: 0,
NumServices: 1,
},

@ -39,10 +39,19 @@ func request_WebhookService_ListWebhooks_0(ctx context.Context, marshaler runtim
var (
protoReq ListWebhooksRequest
metadata runtime.ServerMetadata
err error
)
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
val, ok := pathParams["parent"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "parent")
}
protoReq.Parent, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "parent", err)
}
msg, err := client.ListWebhooks(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
@ -51,7 +60,16 @@ func local_request_WebhookService_ListWebhooks_0(ctx context.Context, marshaler
var (
protoReq ListWebhooksRequest
metadata runtime.ServerMetadata
err error
)
val, ok := pathParams["parent"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "parent")
}
protoReq.Parent, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "parent", err)
}
msg, err := server.ListWebhooks(ctx, &protoReq)
return msg, metadata, err
}
@ -95,12 +113,13 @@ func local_request_WebhookService_GetWebhook_0(ctx context.Context, marshaler ru
return msg, metadata, err
}
var filter_WebhookService_CreateWebhook_0 = &utilities.DoubleArray{Encoding: map[string]int{"webhook": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}}
var filter_WebhookService_CreateWebhook_0 = &utilities.DoubleArray{Encoding: map[string]int{"webhook": 0, "parent": 1}, Base: []int{1, 1, 2, 0, 0}, Check: []int{0, 1, 1, 2, 3}}
func request_WebhookService_CreateWebhook_0(ctx context.Context, marshaler runtime.Marshaler, client WebhookServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq CreateWebhookRequest
metadata runtime.ServerMetadata
err error
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq.Webhook); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
@ -108,6 +127,14 @@ func request_WebhookService_CreateWebhook_0(ctx context.Context, marshaler runti
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
val, ok := pathParams["parent"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "parent")
}
protoReq.Parent, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "parent", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -122,10 +149,19 @@ func local_request_WebhookService_CreateWebhook_0(ctx context.Context, marshaler
var (
protoReq CreateWebhookRequest
metadata runtime.ServerMetadata
err error
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq.Webhook); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
val, ok := pathParams["parent"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "parent")
}
protoReq.Parent, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "parent", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -268,7 +304,7 @@ func RegisterWebhookServiceHandlerServer(ctx context.Context, mux *runtime.Serve
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.WebhookService/ListWebhooks", runtime.WithHTTPPathPattern("/api/v1/webhooks"))
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.WebhookService/ListWebhooks", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/webhooks"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
@ -288,7 +324,7 @@ func RegisterWebhookServiceHandlerServer(ctx context.Context, mux *runtime.Serve
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.WebhookService/GetWebhook", runtime.WithHTTPPathPattern("/api/v1/{name=webhooks/*}"))
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.WebhookService/GetWebhook", runtime.WithHTTPPathPattern("/api/v1/{name=users/*/webhooks/*}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
@ -308,7 +344,7 @@ func RegisterWebhookServiceHandlerServer(ctx context.Context, mux *runtime.Serve
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.WebhookService/CreateWebhook", runtime.WithHTTPPathPattern("/api/v1/webhooks"))
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.WebhookService/CreateWebhook", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/webhooks"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
@ -328,7 +364,7 @@ func RegisterWebhookServiceHandlerServer(ctx context.Context, mux *runtime.Serve
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.WebhookService/UpdateWebhook", runtime.WithHTTPPathPattern("/api/v1/{webhook.name=webhooks/*}"))
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.WebhookService/UpdateWebhook", runtime.WithHTTPPathPattern("/api/v1/{webhook.name=users/*/webhooks/*}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
@ -348,7 +384,7 @@ func RegisterWebhookServiceHandlerServer(ctx context.Context, mux *runtime.Serve
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.WebhookService/DeleteWebhook", runtime.WithHTTPPathPattern("/api/v1/{name=webhooks/*}"))
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.WebhookService/DeleteWebhook", runtime.WithHTTPPathPattern("/api/v1/{name=users/*/webhooks/*}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
@ -406,7 +442,7 @@ func RegisterWebhookServiceHandlerClient(ctx context.Context, mux *runtime.Serve
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.WebhookService/ListWebhooks", runtime.WithHTTPPathPattern("/api/v1/webhooks"))
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.WebhookService/ListWebhooks", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/webhooks"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
@ -423,7 +459,7 @@ func RegisterWebhookServiceHandlerClient(ctx context.Context, mux *runtime.Serve
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.WebhookService/GetWebhook", runtime.WithHTTPPathPattern("/api/v1/{name=webhooks/*}"))
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.WebhookService/GetWebhook", runtime.WithHTTPPathPattern("/api/v1/{name=users/*/webhooks/*}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
@ -440,7 +476,7 @@ func RegisterWebhookServiceHandlerClient(ctx context.Context, mux *runtime.Serve
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.WebhookService/CreateWebhook", runtime.WithHTTPPathPattern("/api/v1/webhooks"))
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.WebhookService/CreateWebhook", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/webhooks"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
@ -457,7 +493,7 @@ func RegisterWebhookServiceHandlerClient(ctx context.Context, mux *runtime.Serve
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.WebhookService/UpdateWebhook", runtime.WithHTTPPathPattern("/api/v1/{webhook.name=webhooks/*}"))
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.WebhookService/UpdateWebhook", runtime.WithHTTPPathPattern("/api/v1/{webhook.name=users/*/webhooks/*}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
@ -474,7 +510,7 @@ func RegisterWebhookServiceHandlerClient(ctx context.Context, mux *runtime.Serve
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.WebhookService/DeleteWebhook", runtime.WithHTTPPathPattern("/api/v1/{name=webhooks/*}"))
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.WebhookService/DeleteWebhook", runtime.WithHTTPPathPattern("/api/v1/{name=users/*/webhooks/*}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
@ -491,11 +527,11 @@ func RegisterWebhookServiceHandlerClient(ctx context.Context, mux *runtime.Serve
}
var (
pattern_WebhookService_ListWebhooks_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "webhooks"}, ""))
pattern_WebhookService_GetWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "webhooks", "name"}, ""))
pattern_WebhookService_CreateWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "webhooks"}, ""))
pattern_WebhookService_UpdateWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "webhooks", "webhook.name"}, ""))
pattern_WebhookService_DeleteWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "webhooks", "name"}, ""))
pattern_WebhookService_ListWebhooks_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "webhooks"}, ""))
pattern_WebhookService_GetWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "webhooks", "name"}, ""))
pattern_WebhookService_CreateWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "webhooks"}, ""))
pattern_WebhookService_UpdateWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "webhooks", "webhook.name"}, ""))
pattern_WebhookService_DeleteWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "webhooks", "name"}, ""))
)
var (

@ -31,15 +31,15 @@ const (
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type WebhookServiceClient interface {
// ListWebhooks returns a list of webhooks.
// ListWebhooks returns a list of webhooks for a user.
ListWebhooks(ctx context.Context, in *ListWebhooksRequest, opts ...grpc.CallOption) (*ListWebhooksResponse, error)
// GetWebhook gets a webhook by name.
GetWebhook(ctx context.Context, in *GetWebhookRequest, opts ...grpc.CallOption) (*Webhook, error)
// CreateWebhook creates a new webhook.
// CreateWebhook creates a new webhook for a user.
CreateWebhook(ctx context.Context, in *CreateWebhookRequest, opts ...grpc.CallOption) (*Webhook, error)
// UpdateWebhook updates a webhook.
// UpdateWebhook updates a webhook for a user.
UpdateWebhook(ctx context.Context, in *UpdateWebhookRequest, opts ...grpc.CallOption) (*Webhook, error)
// DeleteWebhook deletes a webhook.
// DeleteWebhook deletes a webhook for a user.
DeleteWebhook(ctx context.Context, in *DeleteWebhookRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
}
@ -105,15 +105,15 @@ func (c *webhookServiceClient) DeleteWebhook(ctx context.Context, in *DeleteWebh
// All implementations must embed UnimplementedWebhookServiceServer
// for forward compatibility.
type WebhookServiceServer interface {
// ListWebhooks returns a list of webhooks.
// ListWebhooks returns a list of webhooks for a user.
ListWebhooks(context.Context, *ListWebhooksRequest) (*ListWebhooksResponse, error)
// GetWebhook gets a webhook by name.
GetWebhook(context.Context, *GetWebhookRequest) (*Webhook, error)
// CreateWebhook creates a new webhook.
// CreateWebhook creates a new webhook for a user.
CreateWebhook(context.Context, *CreateWebhookRequest) (*Webhook, error)
// UpdateWebhook updates a webhook.
// UpdateWebhook updates a webhook for a user.
UpdateWebhook(context.Context, *UpdateWebhookRequest) (*Webhook, error)
// DeleteWebhook deletes a webhook.
// DeleteWebhook deletes a webhook for a user.
DeleteWebhook(context.Context, *DeleteWebhookRequest) (*emptypb.Empty, error)
mustEmbedUnimplementedWebhookServiceServer()
}

@ -602,57 +602,6 @@ paths:
type: string
tags:
- UserService
/api/v1/webhooks:
get:
summary: ListWebhooks returns a list of webhooks.
operationId: WebhookService_ListWebhooks
responses:
"200":
description: A successful response.
schema:
$ref: '#/definitions/v1ListWebhooksResponse'
default:
description: An unexpected error response.
schema:
$ref: '#/definitions/googlerpcStatus'
tags:
- WebhookService
post:
summary: CreateWebhook creates a new webhook.
operationId: WebhookService_CreateWebhook
responses:
"200":
description: A successful response.
schema:
$ref: '#/definitions/v1Webhook'
default:
description: An unexpected error response.
schema:
$ref: '#/definitions/googlerpcStatus'
parameters:
- name: webhook
description: Required. The webhook to create.
in: body
required: true
schema:
$ref: '#/definitions/v1Webhook'
required:
- webhook
- name: webhookId
description: |-
Optional. The webhook ID to use for this webhook.
If empty, a unique ID will be generated.
Must match the pattern [a-z0-9-]+
in: query
required: false
type: string
- name: validateOnly
description: Optional. If set, validate the request but don't actually create the webhook.
in: query
required: false
type: boolean
tags:
- WebhookService
/api/v1/workspace/profile:
get:
summary: Gets the workspace profile.
@ -1242,7 +1191,7 @@ paths:
"200":
description: A successful response.
schema:
$ref: '#/definitions/v1Webhook'
$ref: '#/definitions/apiv1Webhook'
default:
description: An unexpected error response.
schema:
@ -1250,12 +1199,12 @@ paths:
parameters:
- name: name_6
description: |-
Required. The resource name of the webhook.
Format: webhooks/{webhook}
Required. The resource name of the webhook to retrieve.
Format: users/{user}/webhooks/{webhook}
in: path
required: true
type: string
pattern: webhooks/[^/]+
pattern: users/[^/]+/webhooks/[^/]+
tags:
- WebhookService
delete:
@ -1362,7 +1311,7 @@ paths:
- ShortcutService
/api/v1/{name_9}:
delete:
summary: DeleteWebhook deletes a webhook.
summary: DeleteWebhook deletes a webhook for a user.
operationId: WebhookService_DeleteWebhook
responses:
"200":
@ -1378,11 +1327,11 @@ paths:
- name: name_9
description: |-
Required. The resource name of the webhook to delete.
Format: webhooks/{webhook}
Format: users/{user}/webhooks/{webhook}
in: path
required: true
type: string
pattern: webhooks/[^/]+
pattern: users/[^/]+/webhooks/[^/]+
tags:
- WebhookService
/api/v1/{name}:
@ -2131,6 +2080,66 @@ paths:
$ref: '#/definitions/MemoServiceRenameMemoTagBody'
tags:
- MemoService
/api/v1/{parent}/webhooks:
get:
summary: ListWebhooks returns a list of webhooks for a user.
operationId: WebhookService_ListWebhooks
responses:
"200":
description: A successful response.
schema:
$ref: '#/definitions/v1ListWebhooksResponse'
default:
description: An unexpected error response.
schema:
$ref: '#/definitions/googlerpcStatus'
parameters:
- name: parent
description: |-
Required. The parent resource where webhooks are listed.
Format: users/{user}
in: path
required: true
type: string
pattern: users/[^/]+
tags:
- WebhookService
post:
summary: CreateWebhook creates a new webhook for a user.
operationId: WebhookService_CreateWebhook
responses:
"200":
description: A successful response.
schema:
$ref: '#/definitions/apiv1Webhook'
default:
description: An unexpected error response.
schema:
$ref: '#/definitions/googlerpcStatus'
parameters:
- name: parent
description: |-
Required. The parent resource where this webhook will be created.
Format: users/{user}
in: path
required: true
type: string
pattern: users/[^/]+
- name: webhook
description: Required. The webhook to create.
in: body
required: true
schema:
$ref: '#/definitions/apiv1Webhook'
required:
- webhook
- name: validateOnly
description: Optional. If set, validate the request, but do not actually create the webhook.
in: query
required: false
type: boolean
tags:
- WebhookService
/api/v1/{setting.name}:
patch:
summary: Updates a workspace setting.
@ -2333,13 +2342,13 @@ paths:
- UserService
/api/v1/{webhook.name}:
patch:
summary: UpdateWebhook updates a webhook.
summary: UpdateWebhook updates a webhook for a user.
operationId: WebhookService_UpdateWebhook
responses:
"200":
description: A successful response.
schema:
$ref: '#/definitions/v1Webhook'
$ref: '#/definitions/apiv1Webhook'
default:
description: An unexpected error response.
schema:
@ -2348,13 +2357,13 @@ paths:
- name: webhook.name
description: |-
The resource name of the webhook.
Format: webhooks/{webhook}
Format: users/{user}/webhooks/{webhook}
in: path
required: true
type: string
pattern: webhooks/[^/]+
pattern: users/[^/]+/webhooks/[^/]+
- name: webhook
description: Required. The webhook to update.
description: Required. The webhook resource which replaces the resource on the server.
in: body
required: true
schema:
@ -2362,34 +2371,14 @@ paths:
properties:
displayName:
type: string
description: Required. The display name of the webhook.
description: The display name of the webhook.
url:
type: string
description: Required. The target URL for the webhook.
creator:
type: string
title: |-
Output only. The resource name of the creator.
Format: users/{user}
readOnly: true
state:
$ref: '#/definitions/v1State'
description: The state of the webhook.
createTime:
type: string
format: date-time
description: Output only. The creation timestamp.
readOnly: true
updateTime:
type: string
format: date-time
description: Output only. The last update timestamp.
readOnly: true
title: Required. The webhook to update.
description: The target URL for the webhook.
title: Required. The webhook resource which replaces the resource on the server.
required:
- displayName
- url
- state
- webhook
tags:
- WebhookService
@ -2878,6 +2867,23 @@ definitions:
type: string
description: The default visibility of the memo.
title: User settings message
apiv1Webhook:
type: object
properties:
name:
type: string
title: |-
The resource name of the webhook.
Format: users/{user}/webhooks/{webhook}
displayName:
type: string
description: The display name of the webhook.
url:
type: string
description: The target URL for the webhook.
required:
- displayName
- url
apiv1WorkspaceCustomProfile:
type: object
properties:
@ -3658,7 +3664,7 @@ definitions:
type: array
items:
type: object
$ref: '#/definitions/v1Webhook'
$ref: '#/definitions/apiv1Webhook'
description: The list of webhooks.
v1MathBlockNode:
type: object
@ -4200,43 +4206,6 @@ definitions:
- PROTECTED
- PUBLIC
default: VISIBILITY_UNSPECIFIED
v1Webhook:
type: object
properties:
name:
type: string
title: |-
The resource name of the webhook.
Format: webhooks/{webhook}
displayName:
type: string
description: Required. The display name of the webhook.
url:
type: string
description: Required. The target URL for the webhook.
creator:
type: string
title: |-
Output only. The resource name of the creator.
Format: users/{user}
readOnly: true
state:
$ref: '#/definitions/v1State'
description: The state of the webhook.
createTime:
type: string
format: date-time
description: Output only. The creation timestamp.
readOnly: true
updateTime:
type: string
format: date-time
description: Output only. The last update timestamp.
readOnly: true
required:
- displayName
- url
- state
v1WorkspaceProfile:
type: object
properties:

@ -34,6 +34,8 @@ const (
UserSetting_ACCESS_TOKENS UserSetting_Key = 3
// The shortcuts of the user.
UserSetting_SHORTCUTS UserSetting_Key = 4
// The webhooks of the user.
UserSetting_WEBHOOKS UserSetting_Key = 5
)
// Enum value maps for UserSetting_Key.
@ -44,6 +46,7 @@ var (
2: "SESSIONS",
3: "ACCESS_TOKENS",
4: "SHORTCUTS",
5: "WEBHOOKS",
}
UserSetting_Key_value = map[string]int32{
"KEY_UNSPECIFIED": 0,
@ -51,6 +54,7 @@ var (
"SESSIONS": 2,
"ACCESS_TOKENS": 3,
"SHORTCUTS": 4,
"WEBHOOKS": 5,
}
)
@ -91,6 +95,7 @@ type UserSetting struct {
// *UserSetting_Sessions
// *UserSetting_AccessTokens
// *UserSetting_Shortcuts
// *UserSetting_Webhooks
Value isUserSetting_Value `protobuf_oneof:"value"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
@ -183,6 +188,15 @@ func (x *UserSetting) GetShortcuts() *ShortcutsUserSetting {
return nil
}
func (x *UserSetting) GetWebhooks() *WebhooksUserSetting {
if x != nil {
if x, ok := x.Value.(*UserSetting_Webhooks); ok {
return x.Webhooks
}
}
return nil
}
type isUserSetting_Value interface {
isUserSetting_Value()
}
@ -203,6 +217,10 @@ type UserSetting_Shortcuts struct {
Shortcuts *ShortcutsUserSetting `protobuf:"bytes,6,opt,name=shortcuts,proto3,oneof"`
}
type UserSetting_Webhooks struct {
Webhooks *WebhooksUserSetting `protobuf:"bytes,7,opt,name=webhooks,proto3,oneof"`
}
func (*UserSetting_General) isUserSetting_Value() {}
func (*UserSetting_Sessions) isUserSetting_Value() {}
@ -211,6 +229,8 @@ func (*UserSetting_AccessTokens) isUserSetting_Value() {}
func (*UserSetting_Shortcuts) isUserSetting_Value() {}
func (*UserSetting_Webhooks) isUserSetting_Value() {}
type GeneralUserSetting struct {
state protoimpl.MessageState `protogen:"open.v1"`
// The user's locale.
@ -406,6 +426,50 @@ func (x *ShortcutsUserSetting) GetShortcuts() []*ShortcutsUserSetting_Shortcut {
return nil
}
type WebhooksUserSetting struct {
state protoimpl.MessageState `protogen:"open.v1"`
Webhooks []*WebhooksUserSetting_Webhook `protobuf:"bytes,1,rep,name=webhooks,proto3" json:"webhooks,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *WebhooksUserSetting) Reset() {
*x = WebhooksUserSetting{}
mi := &file_store_user_setting_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *WebhooksUserSetting) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*WebhooksUserSetting) ProtoMessage() {}
func (x *WebhooksUserSetting) ProtoReflect() protoreflect.Message {
mi := &file_store_user_setting_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use WebhooksUserSetting.ProtoReflect.Descriptor instead.
func (*WebhooksUserSetting) Descriptor() ([]byte, []int) {
return file_store_user_setting_proto_rawDescGZIP(), []int{5}
}
func (x *WebhooksUserSetting) GetWebhooks() []*WebhooksUserSetting_Webhook {
if x != nil {
return x.Webhooks
}
return nil
}
type SessionsUserSetting_Session struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Unique session identifier.
@ -424,7 +488,7 @@ type SessionsUserSetting_Session struct {
func (x *SessionsUserSetting_Session) Reset() {
*x = SessionsUserSetting_Session{}
mi := &file_store_user_setting_proto_msgTypes[5]
mi := &file_store_user_setting_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -436,7 +500,7 @@ func (x *SessionsUserSetting_Session) String() string {
func (*SessionsUserSetting_Session) ProtoMessage() {}
func (x *SessionsUserSetting_Session) ProtoReflect() protoreflect.Message {
mi := &file_store_user_setting_proto_msgTypes[5]
mi := &file_store_user_setting_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -505,7 +569,7 @@ type SessionsUserSetting_ClientInfo struct {
func (x *SessionsUserSetting_ClientInfo) Reset() {
*x = SessionsUserSetting_ClientInfo{}
mi := &file_store_user_setting_proto_msgTypes[6]
mi := &file_store_user_setting_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -517,7 +581,7 @@ func (x *SessionsUserSetting_ClientInfo) String() string {
func (*SessionsUserSetting_ClientInfo) ProtoMessage() {}
func (x *SessionsUserSetting_ClientInfo) ProtoReflect() protoreflect.Message {
mi := &file_store_user_setting_proto_msgTypes[6]
mi := &file_store_user_setting_proto_msgTypes[7]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -581,7 +645,7 @@ type AccessTokensUserSetting_AccessToken struct {
func (x *AccessTokensUserSetting_AccessToken) Reset() {
*x = AccessTokensUserSetting_AccessToken{}
mi := &file_store_user_setting_proto_msgTypes[7]
mi := &file_store_user_setting_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -593,7 +657,7 @@ func (x *AccessTokensUserSetting_AccessToken) String() string {
func (*AccessTokensUserSetting_AccessToken) ProtoMessage() {}
func (x *AccessTokensUserSetting_AccessToken) ProtoReflect() protoreflect.Message {
mi := &file_store_user_setting_proto_msgTypes[7]
mi := &file_store_user_setting_proto_msgTypes[8]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -634,7 +698,7 @@ type ShortcutsUserSetting_Shortcut struct {
func (x *ShortcutsUserSetting_Shortcut) Reset() {
*x = ShortcutsUserSetting_Shortcut{}
mi := &file_store_user_setting_proto_msgTypes[8]
mi := &file_store_user_setting_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -646,7 +710,7 @@ func (x *ShortcutsUserSetting_Shortcut) String() string {
func (*ShortcutsUserSetting_Shortcut) ProtoMessage() {}
func (x *ShortcutsUserSetting_Shortcut) ProtoReflect() protoreflect.Message {
mi := &file_store_user_setting_proto_msgTypes[8]
mi := &file_store_user_setting_proto_msgTypes[9]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -683,24 +747,89 @@ func (x *ShortcutsUserSetting_Shortcut) GetFilter() string {
return ""
}
type WebhooksUserSetting_Webhook struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Unique identifier for the webhook
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
// Descriptive title for the webhook
Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"`
// The webhook URL endpoint
Url string `protobuf:"bytes,3,opt,name=url,proto3" json:"url,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *WebhooksUserSetting_Webhook) Reset() {
*x = WebhooksUserSetting_Webhook{}
mi := &file_store_user_setting_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *WebhooksUserSetting_Webhook) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*WebhooksUserSetting_Webhook) ProtoMessage() {}
func (x *WebhooksUserSetting_Webhook) ProtoReflect() protoreflect.Message {
mi := &file_store_user_setting_proto_msgTypes[10]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use WebhooksUserSetting_Webhook.ProtoReflect.Descriptor instead.
func (*WebhooksUserSetting_Webhook) Descriptor() ([]byte, []int) {
return file_store_user_setting_proto_rawDescGZIP(), []int{5, 0}
}
func (x *WebhooksUserSetting_Webhook) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *WebhooksUserSetting_Webhook) GetTitle() string {
if x != nil {
return x.Title
}
return ""
}
func (x *WebhooksUserSetting_Webhook) GetUrl() string {
if x != nil {
return x.Url
}
return ""
}
var File_store_user_setting_proto protoreflect.FileDescriptor
const file_store_user_setting_proto_rawDesc = "" +
"\n" +
"\x18store/user_setting.proto\x12\vmemos.store\x1a\x1fgoogle/protobuf/timestamp.proto\"\xc5\x03\n" +
"\x18store/user_setting.proto\x12\vmemos.store\x1a\x1fgoogle/protobuf/timestamp.proto\"\x93\x04\n" +
"\vUserSetting\x12\x17\n" +
"\auser_id\x18\x01 \x01(\x05R\x06userId\x12.\n" +
"\x03key\x18\x02 \x01(\x0e2\x1c.memos.store.UserSetting.KeyR\x03key\x12;\n" +
"\ageneral\x18\x03 \x01(\v2\x1f.memos.store.GeneralUserSettingH\x00R\ageneral\x12>\n" +
"\bsessions\x18\x04 \x01(\v2 .memos.store.SessionsUserSettingH\x00R\bsessions\x12K\n" +
"\raccess_tokens\x18\x05 \x01(\v2$.memos.store.AccessTokensUserSettingH\x00R\faccessTokens\x12A\n" +
"\tshortcuts\x18\x06 \x01(\v2!.memos.store.ShortcutsUserSettingH\x00R\tshortcuts\"W\n" +
"\tshortcuts\x18\x06 \x01(\v2!.memos.store.ShortcutsUserSettingH\x00R\tshortcuts\x12>\n" +
"\bwebhooks\x18\a \x01(\v2 .memos.store.WebhooksUserSettingH\x00R\bwebhooks\"e\n" +
"\x03Key\x12\x13\n" +
"\x0fKEY_UNSPECIFIED\x10\x00\x12\v\n" +
"\aGENERAL\x10\x01\x12\f\n" +
"\bSESSIONS\x10\x02\x12\x11\n" +
"\rACCESS_TOKENS\x10\x03\x12\r\n" +
"\tSHORTCUTS\x10\x04B\a\n" +
"\tSHORTCUTS\x10\x04\x12\f\n" +
"\bWEBHOOKS\x10\x05B\a\n" +
"\x05value\"u\n" +
"\x12GeneralUserSetting\x12\x16\n" +
"\x06locale\x18\x01 \x01(\tR\x06locale\x12\x1e\n" +
@ -740,7 +869,13 @@ const file_store_user_setting_proto_rawDesc = "" +
"\bShortcut\x12\x0e\n" +
"\x02id\x18\x01 \x01(\tR\x02id\x12\x14\n" +
"\x05title\x18\x02 \x01(\tR\x05title\x12\x16\n" +
"\x06filter\x18\x03 \x01(\tR\x06filterB\x9b\x01\n" +
"\x06filter\x18\x03 \x01(\tR\x06filter\"\x9e\x01\n" +
"\x13WebhooksUserSetting\x12D\n" +
"\bwebhooks\x18\x01 \x03(\v2(.memos.store.WebhooksUserSetting.WebhookR\bwebhooks\x1aA\n" +
"\aWebhook\x12\x0e\n" +
"\x02id\x18\x01 \x01(\tR\x02id\x12\x14\n" +
"\x05title\x18\x02 \x01(\tR\x05title\x12\x10\n" +
"\x03url\x18\x03 \x01(\tR\x03urlB\x9b\x01\n" +
"\x0fcom.memos.storeB\x10UserSettingProtoP\x01Z)github.com/usememos/memos/proto/gen/store\xa2\x02\x03MSX\xaa\x02\vMemos.Store\xca\x02\vMemos\\Store\xe2\x02\x17Memos\\Store\\GPBMetadata\xea\x02\fMemos::Storeb\x06proto3"
var (
@ -756,7 +891,7 @@ func file_store_user_setting_proto_rawDescGZIP() []byte {
}
var file_store_user_setting_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_store_user_setting_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
var file_store_user_setting_proto_msgTypes = make([]protoimpl.MessageInfo, 11)
var file_store_user_setting_proto_goTypes = []any{
(UserSetting_Key)(0), // 0: memos.store.UserSetting.Key
(*UserSetting)(nil), // 1: memos.store.UserSetting
@ -764,11 +899,13 @@ var file_store_user_setting_proto_goTypes = []any{
(*SessionsUserSetting)(nil), // 3: memos.store.SessionsUserSetting
(*AccessTokensUserSetting)(nil), // 4: memos.store.AccessTokensUserSetting
(*ShortcutsUserSetting)(nil), // 5: memos.store.ShortcutsUserSetting
(*SessionsUserSetting_Session)(nil), // 6: memos.store.SessionsUserSetting.Session
(*SessionsUserSetting_ClientInfo)(nil), // 7: memos.store.SessionsUserSetting.ClientInfo
(*AccessTokensUserSetting_AccessToken)(nil), // 8: memos.store.AccessTokensUserSetting.AccessToken
(*ShortcutsUserSetting_Shortcut)(nil), // 9: memos.store.ShortcutsUserSetting.Shortcut
(*timestamppb.Timestamp)(nil), // 10: google.protobuf.Timestamp
(*WebhooksUserSetting)(nil), // 6: memos.store.WebhooksUserSetting
(*SessionsUserSetting_Session)(nil), // 7: memos.store.SessionsUserSetting.Session
(*SessionsUserSetting_ClientInfo)(nil), // 8: memos.store.SessionsUserSetting.ClientInfo
(*AccessTokensUserSetting_AccessToken)(nil), // 9: memos.store.AccessTokensUserSetting.AccessToken
(*ShortcutsUserSetting_Shortcut)(nil), // 10: memos.store.ShortcutsUserSetting.Shortcut
(*WebhooksUserSetting_Webhook)(nil), // 11: memos.store.WebhooksUserSetting.Webhook
(*timestamppb.Timestamp)(nil), // 12: google.protobuf.Timestamp
}
var file_store_user_setting_proto_depIdxs = []int32{
0, // 0: memos.store.UserSetting.key:type_name -> memos.store.UserSetting.Key
@ -776,18 +913,20 @@ var file_store_user_setting_proto_depIdxs = []int32{
3, // 2: memos.store.UserSetting.sessions:type_name -> memos.store.SessionsUserSetting
4, // 3: memos.store.UserSetting.access_tokens:type_name -> memos.store.AccessTokensUserSetting
5, // 4: memos.store.UserSetting.shortcuts:type_name -> memos.store.ShortcutsUserSetting
6, // 5: memos.store.SessionsUserSetting.sessions:type_name -> memos.store.SessionsUserSetting.Session
8, // 6: memos.store.AccessTokensUserSetting.access_tokens:type_name -> memos.store.AccessTokensUserSetting.AccessToken
9, // 7: memos.store.ShortcutsUserSetting.shortcuts:type_name -> memos.store.ShortcutsUserSetting.Shortcut
10, // 8: memos.store.SessionsUserSetting.Session.create_time:type_name -> google.protobuf.Timestamp
10, // 9: memos.store.SessionsUserSetting.Session.expire_time:type_name -> google.protobuf.Timestamp
10, // 10: memos.store.SessionsUserSetting.Session.last_accessed_time:type_name -> google.protobuf.Timestamp
7, // 11: memos.store.SessionsUserSetting.Session.client_info:type_name -> memos.store.SessionsUserSetting.ClientInfo
12, // [12:12] is the sub-list for method output_type
12, // [12:12] is the sub-list for method input_type
12, // [12:12] is the sub-list for extension type_name
12, // [12:12] is the sub-list for extension extendee
0, // [0:12] is the sub-list for field type_name
6, // 5: memos.store.UserSetting.webhooks:type_name -> memos.store.WebhooksUserSetting
7, // 6: memos.store.SessionsUserSetting.sessions:type_name -> memos.store.SessionsUserSetting.Session
9, // 7: memos.store.AccessTokensUserSetting.access_tokens:type_name -> memos.store.AccessTokensUserSetting.AccessToken
10, // 8: memos.store.ShortcutsUserSetting.shortcuts:type_name -> memos.store.ShortcutsUserSetting.Shortcut
11, // 9: memos.store.WebhooksUserSetting.webhooks:type_name -> memos.store.WebhooksUserSetting.Webhook
12, // 10: memos.store.SessionsUserSetting.Session.create_time:type_name -> google.protobuf.Timestamp
12, // 11: memos.store.SessionsUserSetting.Session.expire_time:type_name -> google.protobuf.Timestamp
12, // 12: memos.store.SessionsUserSetting.Session.last_accessed_time:type_name -> google.protobuf.Timestamp
8, // 13: memos.store.SessionsUserSetting.Session.client_info:type_name -> memos.store.SessionsUserSetting.ClientInfo
14, // [14:14] is the sub-list for method output_type
14, // [14:14] is the sub-list for method input_type
14, // [14:14] is the sub-list for extension type_name
14, // [14:14] is the sub-list for extension extendee
0, // [0:14] is the sub-list for field type_name
}
func init() { file_store_user_setting_proto_init() }
@ -800,6 +939,7 @@ func file_store_user_setting_proto_init() {
(*UserSetting_Sessions)(nil),
(*UserSetting_AccessTokens)(nil),
(*UserSetting_Shortcuts)(nil),
(*UserSetting_Webhooks)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
@ -807,7 +947,7 @@ func file_store_user_setting_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_store_user_setting_proto_rawDesc), len(file_store_user_setting_proto_rawDesc)),
NumEnums: 1,
NumMessages: 9,
NumMessages: 11,
NumExtensions: 0,
NumServices: 0,
},

@ -17,17 +17,19 @@ message UserSetting {
ACCESS_TOKENS = 3;
// The shortcuts of the user.
SHORTCUTS = 4;
// The webhooks of the user.
WEBHOOKS = 5;
}
int32 user_id = 1;
Key key = 2;
oneof value {
GeneralUserSetting general = 3;
SessionsUserSetting sessions = 4;
AccessTokensUserSetting access_tokens = 5;
ShortcutsUserSetting shortcuts = 6;
WebhooksUserSetting webhooks = 7;
}
}
@ -89,3 +91,15 @@ message ShortcutsUserSetting {
}
repeated Shortcut shortcuts = 1;
}
message WebhooksUserSetting {
message Webhook {
// Unique identifier for the webhook
string id = 1;
// Descriptive title for the webhook
string title = 2;
// The webhook URL endpoint
string url = 3;
}
repeated Webhook webhooks = 1;
}

@ -18,7 +18,6 @@ import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/usememos/memos/plugin/webhook"
v1pb "github.com/usememos/memos/proto/gen/api/v1"
@ -689,9 +688,7 @@ func (s *APIV1Service) dispatchMemoRelatedWebhook(ctx context.Context, memo *v1p
if err != nil {
return status.Errorf(codes.InvalidArgument, "invalid memo creator")
}
webhooks, err := s.Store.ListWebhooks(ctx, &store.FindWebhook{
CreatorID: &creatorID,
})
webhooks, err := s.Store.GetUserWebhooks(ctx, creatorID)
if err != nil {
return err
}
@ -701,7 +698,7 @@ func (s *APIV1Service) dispatchMemoRelatedWebhook(ctx context.Context, memo *v1p
return errors.Wrap(err, "failed to convert memo to webhook payload")
}
payload.ActivityType = activityType
payload.Url = hook.URL
payload.Url = hook.Url
// Use asynchronous webhook dispatch
webhook.PostAsync(payload)
@ -709,15 +706,14 @@ func (s *APIV1Service) dispatchMemoRelatedWebhook(ctx context.Context, memo *v1p
return nil
}
func convertMemoToWebhookPayload(memo *v1pb.Memo) (*v1pb.WebhookRequestPayload, error) {
func convertMemoToWebhookPayload(memo *v1pb.Memo) (*webhook.WebhookRequestPayload, error) {
creatorID, err := ExtractUserIDFromName(memo.Creator)
if err != nil {
return nil, errors.Wrap(err, "invalid memo creator")
}
return &v1pb.WebhookRequestPayload{
Creator: fmt.Sprintf("%s%d", UserNamePrefix, creatorID),
CreateTime: timestamppb.New(time.Now()),
Memo: memo,
return &webhook.WebhookRequestPayload{
Creator: fmt.Sprintf("%s%d", UserNamePrefix, creatorID),
Memo: memo,
}, nil
}

@ -146,14 +146,18 @@ func ExtractActivityIDFromName(name string) (int32, error) {
}
// ExtractWebhookIDFromName returns the webhook ID from a resource name.
func ExtractWebhookIDFromName(name string) (int32, error) {
tokens, err := GetNameParentTokens(name, WebhookNamePrefix)
// Expected format: users/{user}/webhooks/{webhook}
func ExtractWebhookIDFromName(name string) (string, error) {
tokens, err := GetNameParentTokens(name, UserNamePrefix, WebhookNamePrefix)
if err != nil {
return 0, err
return "", err
}
id, err := util.ConvertStringToInt32(tokens[0])
if err != nil {
return 0, errors.Errorf("invalid webhook ID %q", tokens[0])
if len(tokens) != 2 {
return "", errors.Errorf("invalid webhook name format: %q", name)
}
return id, nil
webhookID := tokens[1]
if webhookID == "" {
return "", errors.Errorf("invalid webhook ID %q", webhookID)
}
return webhookID, nil
}

@ -13,7 +13,6 @@ import (
func TestCreateWebhook(t *testing.T) {
ctx := context.Background()
t.Run("CreateWebhook with host user", func(t *testing.T) {
// Create test service for this specific test
ts := NewTestService(t)
@ -27,6 +26,7 @@ func TestCreateWebhook(t *testing.T) {
// Create a webhook
req := &v1pb.CreateWebhookRequest{
Parent: fmt.Sprintf("users/%d", hostUser.ID),
Webhook: &v1pb.Webhook{
DisplayName: "Test Webhook",
Url: "https://example.com/webhook",
@ -41,16 +41,16 @@ func TestCreateWebhook(t *testing.T) {
require.Equal(t, "Test Webhook", resp.DisplayName)
require.Equal(t, "https://example.com/webhook", resp.Url)
require.Contains(t, resp.Name, "webhooks/")
require.Equal(t, fmt.Sprintf("users/%d", hostUser.ID), resp.Creator)
require.Contains(t, resp.Name, fmt.Sprintf("users/%d", hostUser.ID))
})
t.Run("CreateWebhook fails without authentication", func(t *testing.T) {
// Create test service for this specific test
ts := NewTestService(t)
defer ts.Cleanup()
// Try to create webhook without authentication
req := &v1pb.CreateWebhookRequest{
Parent: "users/1", // Dummy parent since we don't have a real user
Webhook: &v1pb.Webhook{
DisplayName: "Test Webhook",
Url: "https://example.com/webhook",
@ -73,9 +73,9 @@ func TestCreateWebhook(t *testing.T) {
require.NoError(t, err)
userCtx := ts.CreateUserContext(ctx, regularUser.ID)
// Try to create webhook as regular user
req := &v1pb.CreateWebhookRequest{
Parent: fmt.Sprintf("users/%d", regularUser.ID),
Webhook: &v1pb.Webhook{
DisplayName: "Test Webhook",
Url: "https://example.com/webhook",
@ -99,9 +99,9 @@ func TestCreateWebhook(t *testing.T) {
require.NoError(t, err)
userCtx := ts.CreateUserContext(ctx, hostUser.ID)
// Try to create webhook with missing URL
req := &v1pb.CreateWebhookRequest{
Parent: fmt.Sprintf("users/%d", hostUser.ID),
Webhook: &v1pb.Webhook{
DisplayName: "Test Webhook",
// URL missing
@ -128,9 +128,10 @@ func TestListWebhooks(t *testing.T) {
require.NoError(t, err)
userCtx := ts.CreateUserContext(ctx, hostUser.ID)
// List webhooks
req := &v1pb.ListWebhooksRequest{}
req := &v1pb.ListWebhooksRequest{
Parent: fmt.Sprintf("users/%d", hostUser.ID),
}
resp, err := ts.Service.ListWebhooks(userCtx, req)
// Verify response
@ -148,9 +149,9 @@ func TestListWebhooks(t *testing.T) {
hostUser, err := ts.CreateHostUser(ctx, "admin")
require.NoError(t, err)
userCtx := ts.CreateUserContext(ctx, hostUser.ID)
// Create a webhook
createReq := &v1pb.CreateWebhookRequest{
Parent: fmt.Sprintf("users/%d", hostUser.ID),
Webhook: &v1pb.Webhook{
DisplayName: "Test Webhook",
Url: "https://example.com/webhook",
@ -160,7 +161,9 @@ func TestListWebhooks(t *testing.T) {
require.NoError(t, err)
// List webhooks
listReq := &v1pb.ListWebhooksRequest{}
listReq := &v1pb.ListWebhooksRequest{
Parent: fmt.Sprintf("users/%d", hostUser.ID),
}
resp, err := ts.Service.ListWebhooks(userCtx, listReq)
// Verify response
@ -175,9 +178,10 @@ func TestListWebhooks(t *testing.T) {
// Create test service for this specific test
ts := NewTestService(t)
defer ts.Cleanup()
// Try to list webhooks without authentication
req := &v1pb.ListWebhooksRequest{}
req := &v1pb.ListWebhooksRequest{
Parent: "users/1", // Dummy parent since we don't have a real user
}
_, err := ts.Service.ListWebhooks(ctx, req)
// Should fail with permission denied or unauthenticated
@ -197,9 +201,9 @@ func TestGetWebhook(t *testing.T) {
hostUser, err := ts.CreateHostUser(ctx, "admin")
require.NoError(t, err)
userCtx := ts.CreateUserContext(ctx, hostUser.ID)
// Create a webhook
createReq := &v1pb.CreateWebhookRequest{
Parent: fmt.Sprintf("users/%d", hostUser.ID),
Webhook: &v1pb.Webhook{
DisplayName: "Test Webhook",
Url: "https://example.com/webhook",
@ -213,13 +217,11 @@ func TestGetWebhook(t *testing.T) {
Name: createdWebhook.Name,
}
resp, err := ts.Service.GetWebhook(userCtx, getReq)
// Verify response
require.NoError(t, err)
require.NotNil(t, resp)
require.Equal(t, createdWebhook.Name, resp.Name)
require.Equal(t, createdWebhook.Url, resp.Url)
require.Equal(t, createdWebhook.Creator, resp.Creator)
})
t.Run("GetWebhook fails with invalid name", func(t *testing.T) {
@ -251,10 +253,9 @@ func TestGetWebhook(t *testing.T) {
hostUser, err := ts.CreateHostUser(ctx, "admin")
require.NoError(t, err)
userCtx := ts.CreateUserContext(ctx, hostUser.ID)
// Try to get non-existent webhook
req := &v1pb.GetWebhookRequest{
Name: "webhooks/999",
Name: fmt.Sprintf("users/%d/webhooks/999", hostUser.ID),
}
_, err = ts.Service.GetWebhook(userCtx, req)
@ -276,12 +277,12 @@ func TestUpdateWebhook(t *testing.T) {
hostUser, err := ts.CreateHostUser(ctx, "admin")
require.NoError(t, err)
userCtx := ts.CreateUserContext(ctx, hostUser.ID)
// Create a webhook
createReq := &v1pb.CreateWebhookRequest{
Parent: fmt.Sprintf("users/%d", hostUser.ID),
Webhook: &v1pb.Webhook{
Name: "Original Webhook",
Url: "https://example.com/webhook",
DisplayName: "Original Webhook",
Url: "https://example.com/webhook",
},
}
createdWebhook, err := ts.Service.CreateWebhook(userCtx, createReq)
@ -310,11 +311,10 @@ func TestUpdateWebhook(t *testing.T) {
// Create test service for this specific test
ts := NewTestService(t)
defer ts.Cleanup()
// Try to update webhook without authentication
req := &v1pb.UpdateWebhookRequest{
Webhook: &v1pb.Webhook{
Name: "webhooks/1",
Name: "users/1/webhooks/1",
Url: "https://updated.example.com/webhook",
},
}
@ -328,7 +328,6 @@ func TestUpdateWebhook(t *testing.T) {
func TestDeleteWebhook(t *testing.T) {
ctx := context.Background()
t.Run("DeleteWebhook removes webhook", func(t *testing.T) {
// Create test service for this specific test
ts := NewTestService(t)
@ -341,6 +340,7 @@ func TestDeleteWebhook(t *testing.T) {
// Create a webhook
createReq := &v1pb.CreateWebhookRequest{
Parent: fmt.Sprintf("users/%d", hostUser.ID),
Webhook: &v1pb.Webhook{
DisplayName: "Test Webhook",
Url: "https://example.com/webhook",
@ -373,10 +373,9 @@ func TestDeleteWebhook(t *testing.T) {
// Create test service for this specific test
ts := NewTestService(t)
defer ts.Cleanup()
// Try to delete webhook without authentication
req := &v1pb.DeleteWebhookRequest{
Name: "webhooks/1",
Name: "users/1/webhooks/1",
}
_, err := ts.Service.DeleteWebhook(ctx, req)
@ -394,10 +393,9 @@ func TestDeleteWebhook(t *testing.T) {
hostUser, err := ts.CreateHostUser(ctx, "admin")
require.NoError(t, err)
userCtx := ts.CreateUserContext(ctx, hostUser.ID)
// Try to delete non-existent webhook
req := &v1pb.DeleteWebhookRequest{
Name: "webhooks/999",
Name: fmt.Sprintf("users/%d/webhooks/999", hostUser.ID),
}
_, err = ts.Service.DeleteWebhook(userCtx, req)

@ -2,17 +2,18 @@ package v1
import (
"context"
"crypto/rand"
"encoding/hex"
"fmt"
"strings"
"time"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/usememos/memos/internal/util"
v1pb "github.com/usememos/memos/proto/gen/api/v1"
"github.com/usememos/memos/store"
storepb "github.com/usememos/memos/proto/gen/store"
)
func (s *APIV1Service) CreateWebhook(ctx context.Context, request *v1pb.CreateWebhookRequest) (*v1pb.Webhook, error) {
@ -24,6 +25,17 @@ func (s *APIV1Service) CreateWebhook(ctx context.Context, request *v1pb.CreateWe
return nil, status.Errorf(codes.Unauthenticated, "user not authenticated")
}
// Extract user ID from parent (format: users/{user})
parentUserID, err := ExtractUserIDFromName(request.Parent)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid parent: %v", err)
}
// Users can only create webhooks for themselves
if parentUserID != currentUser.ID {
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
}
// Only host users can create webhooks
if !isSuperUser(currentUser) {
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
@ -37,29 +49,42 @@ func (s *APIV1Service) CreateWebhook(ctx context.Context, request *v1pb.CreateWe
return nil, status.Errorf(codes.InvalidArgument, "webhook URL is required")
}
// TODO: Handle webhook_id, validate_only, and request_id fields
// Handle validate_only field
if request.ValidateOnly {
// Perform validation checks without actually creating the webhook
return &v1pb.Webhook{
Name: fmt.Sprintf("users/%d/webhooks/validate", currentUser.ID),
DisplayName: request.Webhook.DisplayName,
Url: request.Webhook.Url,
Creator: fmt.Sprintf("users/%d", currentUser.ID),
State: request.Webhook.State,
}, nil
}
webhook, err := s.Store.CreateWebhook(ctx, &store.Webhook{
CreatorID: currentUser.ID,
Name: request.Webhook.DisplayName,
URL: strings.TrimSpace(request.Webhook.Url),
err = s.Store.AddUserWebhook(ctx, currentUser.ID, &storepb.WebhooksUserSetting_Webhook{
Id: generateWebhookID(),
Title: request.Webhook.DisplayName,
Url: strings.TrimSpace(request.Webhook.Url),
})
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to create webhook, error: %+v", err)
}
return convertWebhookFromStore(webhook), nil
// Return the newly created webhook
webhooks, err := s.Store.GetUserWebhooks(ctx, currentUser.ID)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get user webhooks, error: %+v", err)
}
// Find the webhook we just created
for _, webhook := range webhooks {
if webhook.Title == request.Webhook.DisplayName && webhook.Url == strings.TrimSpace(request.Webhook.Url) {
return convertWebhookFromUserSetting(webhook, currentUser.ID), nil
}
}
return nil, status.Errorf(codes.Internal, "failed to find created webhook")
}
func (s *APIV1Service) ListWebhooks(ctx context.Context, _ *v1pb.ListWebhooksRequest) (*v1pb.ListWebhooksResponse, error) {
func (s *APIV1Service) ListWebhooks(ctx context.Context, request *v1pb.ListWebhooksRequest) (*v1pb.ListWebhooksResponse, error) {
currentUser, err := s.GetCurrentUser(ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get user: %v", err)
@ -68,11 +93,18 @@ func (s *APIV1Service) ListWebhooks(ctx context.Context, _ *v1pb.ListWebhooksReq
return nil, status.Errorf(codes.Unauthenticated, "user not authenticated")
}
// TODO: Implement proper filtering, ordering, and pagination
// For now, list webhooks for the current user
webhooks, err := s.Store.ListWebhooks(ctx, &store.FindWebhook{
CreatorID: &currentUser.ID,
})
// Extract user ID from parent (format: users/{user})
parentUserID, err := ExtractUserIDFromName(request.Parent)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid parent: %v", err)
}
// Users can only list their own webhooks
if parentUserID != currentUser.ID {
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
}
webhooks, err := s.Store.GetUserWebhooks(ctx, currentUser.ID)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to list webhooks, error: %+v", err)
}
@ -81,16 +113,28 @@ func (s *APIV1Service) ListWebhooks(ctx context.Context, _ *v1pb.ListWebhooksReq
Webhooks: []*v1pb.Webhook{},
}
for _, webhook := range webhooks {
response.Webhooks = append(response.Webhooks, convertWebhookFromStore(webhook))
response.Webhooks = append(response.Webhooks, convertWebhookFromUserSetting(webhook, currentUser.ID))
}
return response, nil
}
func (s *APIV1Service) GetWebhook(ctx context.Context, request *v1pb.GetWebhookRequest) (*v1pb.Webhook, error) {
webhookID, err := ExtractWebhookIDFromName(request.Name)
// Extract user ID and webhook ID from name (format: users/{user}/webhooks/{webhook})
tokens, err := GetNameParentTokens(request.Name, UserNamePrefix, WebhookNamePrefix)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid webhook name: %v", err)
}
if len(tokens) != 2 {
return nil, status.Errorf(codes.InvalidArgument, "invalid webhook name format")
}
userIDStr := tokens[0]
webhookID := tokens[1]
requestedUserID, err := util.ConvertStringToInt32(userIDStr)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid user ID in webhook name: %v", err)
}
currentUser, err := s.GetCurrentUser(ctx)
if err != nil {
@ -100,22 +144,23 @@ func (s *APIV1Service) GetWebhook(ctx context.Context, request *v1pb.GetWebhookR
return nil, status.Errorf(codes.Unauthenticated, "user not authenticated")
}
webhook, err := s.Store.GetWebhook(ctx, &store.FindWebhook{
ID: &webhookID,
CreatorID: &currentUser.ID,
})
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get webhook, error: %+v", err)
}
if webhook == nil {
return nil, status.Errorf(codes.NotFound, "webhook not found")
// Users can only access their own webhooks
if requestedUserID != currentUser.ID {
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
}
webhookPb := convertWebhookFromStore(webhook)
// TODO: Implement read_mask field filtering
webhooks, err := s.Store.GetUserWebhooks(ctx, currentUser.ID)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get webhooks, error: %+v", err)
}
return webhookPb, nil
// Find webhook by ID
for _, webhook := range webhooks {
if webhook.Id == webhookID {
return convertWebhookFromUserSetting(webhook, currentUser.ID), nil
}
}
return nil, status.Errorf(codes.NotFound, "webhook not found")
}
func (s *APIV1Service) UpdateWebhook(ctx context.Context, request *v1pb.UpdateWebhookRequest) (*v1pb.Webhook, error) {
@ -123,10 +168,22 @@ func (s *APIV1Service) UpdateWebhook(ctx context.Context, request *v1pb.UpdateWe
return nil, status.Errorf(codes.InvalidArgument, "update_mask is required")
}
webhookID, err := ExtractWebhookIDFromName(request.Webhook.Name)
// Extract user ID and webhook ID from name (format: users/{user}/webhooks/{webhook})
tokens, err := GetNameParentTokens(request.Webhook.Name, UserNamePrefix, WebhookNamePrefix)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid webhook name: %v", err)
}
if len(tokens) != 2 {
return nil, status.Errorf(codes.InvalidArgument, "invalid webhook name format")
}
userIDStr := tokens[0]
webhookID := tokens[1]
requestedUserID, err := util.ConvertStringToInt32(userIDStr)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid user ID in webhook name: %v", err)
}
currentUser, err := s.GetCurrentUser(ctx)
if err != nil {
@ -136,44 +193,75 @@ func (s *APIV1Service) UpdateWebhook(ctx context.Context, request *v1pb.UpdateWe
return nil, status.Errorf(codes.Unauthenticated, "user not authenticated")
}
// Check if webhook exists and user has permission
existingWebhook, err := s.Store.GetWebhook(ctx, &store.FindWebhook{
ID: &webhookID,
CreatorID: &currentUser.ID,
})
// Users can only update their own webhooks
if requestedUserID != currentUser.ID {
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
}
// Get existing webhooks from user settings
webhooks, err := s.Store.GetUserWebhooks(ctx, currentUser.ID)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get webhook: %v", err)
return nil, status.Errorf(codes.Internal, "failed to get webhooks: %v", err)
}
// Find the webhook to update
var existingWebhook *storepb.WebhooksUserSetting_Webhook
for _, webhook := range webhooks {
if webhook.Id == webhookID {
existingWebhook = webhook
break
}
}
if existingWebhook == nil {
return nil, status.Errorf(codes.NotFound, "webhook not found")
}
update := &store.UpdateWebhook{
ID: webhookID,
// Create updated webhook
updatedWebhook := &storepb.WebhooksUserSetting_Webhook{
Id: existingWebhook.Id,
Title: existingWebhook.Title,
Url: existingWebhook.Url,
}
// Apply updates based on update mask
for _, field := range request.UpdateMask.Paths {
switch field {
case "display_name":
update.Name = &request.Webhook.DisplayName
updatedWebhook.Title = request.Webhook.DisplayName
case "url":
update.URL = &request.Webhook.Url
updatedWebhook.Url = request.Webhook.Url
default:
return nil, status.Errorf(codes.InvalidArgument, "invalid update path: %s", field)
}
}
webhook, err := s.Store.UpdateWebhook(ctx, update)
// Update the webhook in user settings
err = s.Store.UpdateUserWebhook(ctx, currentUser.ID, updatedWebhook)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to update webhook, error: %+v", err)
return nil, status.Errorf(codes.Internal, "failed to update webhook: %v", err)
}
return convertWebhookFromStore(webhook), nil
return convertWebhookFromUserSetting(updatedWebhook, currentUser.ID), nil
}
func (s *APIV1Service) DeleteWebhook(ctx context.Context, request *v1pb.DeleteWebhookRequest) (*emptypb.Empty, error) {
webhookID, err := ExtractWebhookIDFromName(request.Name)
// Extract user ID and webhook ID from name (format: users/{user}/webhooks/{webhook})
tokens, err := GetNameParentTokens(request.Name, UserNamePrefix, WebhookNamePrefix)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid webhook name: %v", err)
}
if len(tokens) != 2 {
return nil, status.Errorf(codes.InvalidArgument, "invalid webhook name format")
}
userIDStr := tokens[0]
webhookID := tokens[1]
requestedUserID, err := util.ConvertStringToInt32(userIDStr)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid user ID in webhook name: %v", err)
}
currentUser, err := s.GetCurrentUser(ctx)
if err != nil {
@ -183,37 +271,47 @@ func (s *APIV1Service) DeleteWebhook(ctx context.Context, request *v1pb.DeleteWe
return nil, status.Errorf(codes.Unauthenticated, "user not authenticated")
}
// Check if webhook exists and user has permission
webhook, err := s.Store.GetWebhook(ctx, &store.FindWebhook{
ID: &webhookID,
CreatorID: &currentUser.ID,
})
// Users can only delete their own webhooks
if requestedUserID != currentUser.ID {
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
}
// Get existing webhooks from user settings to verify it exists
webhooks, err := s.Store.GetUserWebhooks(ctx, currentUser.ID)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get webhook: %v", err)
return nil, status.Errorf(codes.Internal, "failed to get webhooks: %v", err)
}
if webhook == nil {
return nil, status.Errorf(codes.NotFound, "webhook not found")
// Check if webhook exists
webhookExists := false
for _, webhook := range webhooks {
if webhook.Id == webhookID {
webhookExists = true
break
}
}
// TODO: Handle force field properly
if !webhookExists {
return nil, status.Errorf(codes.NotFound, "webhook not found")
}
err = s.Store.DeleteWebhook(ctx, &store.DeleteWebhook{
ID: webhookID,
})
err = s.Store.RemoveUserWebhook(ctx, currentUser.ID, webhookID)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to delete webhook, error: %+v", err)
return nil, status.Errorf(codes.Internal, "failed to delete webhook: %v", err)
}
return &emptypb.Empty{}, nil
}
func convertWebhookFromStore(webhook *store.Webhook) *v1pb.Webhook {
func convertWebhookFromUserSetting(webhook *storepb.WebhooksUserSetting_Webhook, userID int32) *v1pb.Webhook {
return &v1pb.Webhook{
Name: fmt.Sprintf("webhooks/%d", webhook.ID),
DisplayName: webhook.Name,
Url: webhook.URL,
Creator: fmt.Sprintf("users/%d", webhook.CreatorID),
State: v1pb.State_NORMAL, // Default to NORMAL state for webhooks
CreateTime: timestamppb.New(time.Unix(webhook.CreatedTs, 0)),
UpdateTime: timestamppb.New(time.Unix(webhook.UpdatedTs, 0)),
Name: fmt.Sprintf("users/%d/webhooks/%s", userID, webhook.Id),
DisplayName: webhook.Title,
Url: webhook.Url,
}
}
func generateWebhookID() string {
b := make([]byte, 8)
rand.Read(b)
return hex.EncodeToString(b)
}

@ -1,108 +0,0 @@
package mysql
import (
"context"
"strings"
"github.com/usememos/memos/store"
)
func (d *DB) CreateWebhook(ctx context.Context, create *store.Webhook) (*store.Webhook, error) {
fields := []string{"`name`", "`url`", "`creator_id`"}
placeholder := []string{"?", "?", "?"}
args := []any{create.Name, create.URL, create.CreatorID}
stmt := "INSERT INTO `webhook` (" + strings.Join(fields, ", ") + ") VALUES (" + strings.Join(placeholder, ", ") + ")"
result, err := d.db.ExecContext(ctx, stmt, args...)
if err != nil {
return nil, err
}
id, err := result.LastInsertId()
if err != nil {
return nil, err
}
create.ID = int32(id)
return d.GetWebhook(ctx, &store.FindWebhook{ID: &create.ID})
}
func (d *DB) ListWebhooks(ctx context.Context, find *store.FindWebhook) ([]*store.Webhook, error) {
where, args := []string{"1 = 1"}, []any{}
if find.ID != nil {
where, args = append(where, "`id` = ?"), append(args, *find.ID)
}
if find.CreatorID != nil {
where, args = append(where, "`creator_id` = ?"), append(args, *find.CreatorID)
}
rows, err := d.db.QueryContext(ctx, "SELECT `id`, UNIX_TIMESTAMP(`created_ts`), UNIX_TIMESTAMP(`updated_ts`), `creator_id`, `name`, `url` FROM `webhook` WHERE "+strings.Join(where, " AND ")+" ORDER BY `id` DESC",
args...,
)
if err != nil {
return nil, err
}
defer rows.Close()
list := []*store.Webhook{}
for rows.Next() {
webhook := &store.Webhook{}
if err := rows.Scan(
&webhook.ID,
&webhook.CreatedTs,
&webhook.UpdatedTs,
&webhook.CreatorID,
&webhook.Name,
&webhook.URL,
); err != nil {
return nil, err
}
list = append(list, webhook)
}
if err := rows.Err(); err != nil {
return nil, err
}
return list, nil
}
func (d *DB) GetWebhook(ctx context.Context, find *store.FindWebhook) (*store.Webhook, error) {
list, err := d.ListWebhooks(ctx, find)
if err != nil {
return nil, err
}
if len(list) == 0 {
return nil, nil
}
return list[0], nil
}
func (d *DB) UpdateWebhook(ctx context.Context, update *store.UpdateWebhook) (*store.Webhook, error) {
set, args := []string{}, []any{}
if update.Name != nil {
set, args = append(set, "`name` = ?"), append(args, *update.Name)
}
if update.URL != nil {
set, args = append(set, "`url` = ?"), append(args, *update.URL)
}
args = append(args, update.ID)
stmt := "UPDATE `webhook` SET " + strings.Join(set, ", ") + " WHERE `id` = ?"
_, err := d.db.ExecContext(ctx, stmt, args...)
if err != nil {
return nil, err
}
webhook, err := d.GetWebhook(ctx, &store.FindWebhook{ID: &update.ID})
if err != nil {
return nil, err
}
return webhook, nil
}
func (d *DB) DeleteWebhook(ctx context.Context, delete *store.DeleteWebhook) error {
_, err := d.db.ExecContext(ctx, "DELETE FROM `webhook` WHERE `id` = ?", delete.ID)
return err
}

@ -1,103 +0,0 @@
package postgres
import (
"context"
"strings"
"github.com/usememos/memos/store"
)
func (d *DB) CreateWebhook(ctx context.Context, create *store.Webhook) (*store.Webhook, error) {
fields := []string{"name", "url", "creator_id"}
args := []any{create.Name, create.URL, create.CreatorID}
stmt := "INSERT INTO webhook (" + strings.Join(fields, ", ") + ") VALUES (" + placeholders(len(args)) + ") RETURNING id, created_ts, updated_ts"
if err := d.db.QueryRowContext(ctx, stmt, args...).Scan(
&create.ID,
&create.CreatedTs,
&create.UpdatedTs,
); err != nil {
return nil, err
}
webhook := create
return webhook, nil
}
func (d *DB) ListWebhooks(ctx context.Context, find *store.FindWebhook) ([]*store.Webhook, error) {
where, args := []string{"1 = 1"}, []any{}
if find.ID != nil {
where, args = append(where, "id = "+placeholder(len(args)+1)), append(args, *find.ID)
}
if find.CreatorID != nil {
where, args = append(where, "creator_id = "+placeholder(len(args)+1)), append(args, *find.CreatorID)
}
rows, err := d.db.QueryContext(ctx, `
SELECT
id,
created_ts,
updated_ts,
creator_id,
name,
url
FROM webhook
WHERE `+strings.Join(where, " AND ")+`
ORDER BY id DESC`,
args...,
)
if err != nil {
return nil, err
}
defer rows.Close()
list := []*store.Webhook{}
for rows.Next() {
webhook := &store.Webhook{}
if err := rows.Scan(
&webhook.ID,
&webhook.CreatedTs,
&webhook.UpdatedTs,
&webhook.CreatorID,
&webhook.Name,
&webhook.URL,
); err != nil {
return nil, err
}
list = append(list, webhook)
}
if err := rows.Err(); err != nil {
return nil, err
}
return list, nil
}
func (d *DB) UpdateWebhook(ctx context.Context, update *store.UpdateWebhook) (*store.Webhook, error) {
set, args := []string{}, []any{}
if update.Name != nil {
set, args = append(set, "name = "+placeholder(len(args)+1)), append(args, *update.Name)
}
if update.URL != nil {
set, args = append(set, "url = "+placeholder(len(args)+1)), append(args, *update.URL)
}
stmt := "UPDATE webhook SET " + strings.Join(set, ", ") + " WHERE id = " + placeholder(len(args)+1) + " RETURNING id, created_ts, updated_ts, creator_id, name, url"
args = append(args, update.ID)
webhook := &store.Webhook{}
if err := d.db.QueryRowContext(ctx, stmt, args...).Scan(
&webhook.ID,
&webhook.CreatedTs,
&webhook.UpdatedTs,
&webhook.CreatorID,
&webhook.Name,
&webhook.URL,
); err != nil {
return nil, err
}
return webhook, nil
}
func (d *DB) DeleteWebhook(ctx context.Context, delete *store.DeleteWebhook) error {
_, err := d.db.ExecContext(ctx, "DELETE FROM webhook WHERE id = $1", delete.ID)
return err
}

@ -1,104 +0,0 @@
package sqlite
import (
"context"
"strings"
"github.com/usememos/memos/store"
)
func (d *DB) CreateWebhook(ctx context.Context, create *store.Webhook) (*store.Webhook, error) {
fields := []string{"`name`", "`url`", "`creator_id`"}
placeholder := []string{"?", "?", "?"}
args := []any{create.Name, create.URL, create.CreatorID}
stmt := "INSERT INTO `webhook` (" + strings.Join(fields, ", ") + ") VALUES (" + strings.Join(placeholder, ", ") + ") RETURNING `id`, `created_ts`, `updated_ts`"
if err := d.db.QueryRowContext(ctx, stmt, args...).Scan(
&create.ID,
&create.CreatedTs,
&create.UpdatedTs,
); err != nil {
return nil, err
}
webhook := create
return webhook, nil
}
func (d *DB) ListWebhooks(ctx context.Context, find *store.FindWebhook) ([]*store.Webhook, error) {
where, args := []string{"1 = 1"}, []any{}
if find.ID != nil {
where, args = append(where, "`id` = ?"), append(args, *find.ID)
}
if find.CreatorID != nil {
where, args = append(where, "`creator_id` = ?"), append(args, *find.CreatorID)
}
rows, err := d.db.QueryContext(ctx, `
SELECT
id,
created_ts,
updated_ts,
creator_id,
name,
url
FROM webhook
WHERE `+strings.Join(where, " AND ")+`
ORDER BY id DESC`,
args...,
)
if err != nil {
return nil, err
}
defer rows.Close()
list := []*store.Webhook{}
for rows.Next() {
webhook := &store.Webhook{}
if err := rows.Scan(
&webhook.ID,
&webhook.CreatedTs,
&webhook.UpdatedTs,
&webhook.CreatorID,
&webhook.Name,
&webhook.URL,
); err != nil {
return nil, err
}
list = append(list, webhook)
}
if err := rows.Err(); err != nil {
return nil, err
}
return list, nil
}
func (d *DB) UpdateWebhook(ctx context.Context, update *store.UpdateWebhook) (*store.Webhook, error) {
set, args := []string{}, []any{}
if update.Name != nil {
set, args = append(set, "name = ?"), append(args, *update.Name)
}
if update.URL != nil {
set, args = append(set, "url = ?"), append(args, *update.URL)
}
args = append(args, update.ID)
stmt := "UPDATE `webhook` SET " + strings.Join(set, ", ") + " WHERE `id` = ? RETURNING `id`, `created_ts`, `updated_ts`, `creator_id`, `name`, `url`"
webhook := &store.Webhook{}
if err := d.db.QueryRowContext(ctx, stmt, args...).Scan(
&webhook.ID,
&webhook.CreatedTs,
&webhook.UpdatedTs,
&webhook.CreatorID,
&webhook.Name,
&webhook.URL,
); err != nil {
return nil, err
}
return webhook, nil
}
func (d *DB) DeleteWebhook(ctx context.Context, delete *store.DeleteWebhook) error {
_, err := d.db.ExecContext(ctx, "DELETE FROM `webhook` WHERE `id` = ?", delete.ID)
return err
}

@ -69,12 +69,6 @@ type Driver interface {
UpdateInbox(ctx context.Context, update *UpdateInbox) (*Inbox, error)
DeleteInbox(ctx context.Context, delete *DeleteInbox) error
// Webhook model related methods.
CreateWebhook(ctx context.Context, create *Webhook) (*Webhook, error)
ListWebhooks(ctx context.Context, find *FindWebhook) ([]*Webhook, error)
UpdateWebhook(ctx context.Context, update *UpdateWebhook) (*Webhook, error)
DeleteWebhook(ctx context.Context, delete *DeleteWebhook) error
// Reaction model related methods.
UpsertReaction(ctx context.Context, create *Reaction) (*Reaction, error)
ListReactions(ctx context.Context, find *FindReaction) ([]*Reaction, error)

@ -1,49 +0,0 @@
package teststore
import (
"context"
"testing"
"github.com/stretchr/testify/require"
"github.com/usememos/memos/store"
)
func TestWebhookStore(t *testing.T) {
ctx := context.Background()
ts := NewTestingStore(ctx, t)
user, err := createTestingHostUser(ctx, ts)
require.NoError(t, err)
webhook, err := ts.CreateWebhook(ctx, &store.Webhook{
CreatorID: user.ID,
Name: "test_webhook",
URL: "https://example.com",
})
require.NoError(t, err)
require.Equal(t, "test_webhook", webhook.Name)
require.Equal(t, user.ID, webhook.CreatorID)
webhooks, err := ts.ListWebhooks(ctx, &store.FindWebhook{
CreatorID: &user.ID,
})
require.NoError(t, err)
require.Equal(t, 1, len(webhooks))
require.Equal(t, webhook, webhooks[0])
newName := "test_webhook_new"
updatedWebhook, err := ts.UpdateWebhook(ctx, &store.UpdateWebhook{
ID: webhook.ID,
Name: &newName,
})
require.NoError(t, err)
require.Equal(t, newName, updatedWebhook.Name)
require.Equal(t, webhook.CreatorID, updatedWebhook.CreatorID)
err = ts.DeleteWebhook(ctx, &store.DeleteWebhook{
ID: webhook.ID,
})
require.NoError(t, err)
webhooks, err = ts.ListWebhooks(ctx, &store.FindWebhook{
CreatorID: &user.ID,
})
require.NoError(t, err)
require.Equal(t, 0, len(webhooks))
ts.Close()
}

@ -241,6 +241,114 @@ func (s *Store) UpdateUserSessionLastAccessed(ctx context.Context, userID int32,
return err
}
// GetUserWebhooks returns the webhooks of the user.
func (s *Store) GetUserWebhooks(ctx context.Context, userID int32) ([]*storepb.WebhooksUserSetting_Webhook, error) {
userSetting, err := s.GetUserSetting(ctx, &FindUserSetting{
UserID: &userID,
Key: storepb.UserSetting_WEBHOOKS,
})
if err != nil {
return nil, err
}
if userSetting == nil {
return []*storepb.WebhooksUserSetting_Webhook{}, nil
}
webhooksUserSetting := userSetting.GetWebhooks()
return webhooksUserSetting.Webhooks, nil
}
// AddUserWebhook adds a new webhook for the user.
func (s *Store) AddUserWebhook(ctx context.Context, userID int32, webhook *storepb.WebhooksUserSetting_Webhook) error {
existingWebhooks, err := s.GetUserWebhooks(ctx, userID)
if err != nil {
return err
}
// Check if webhook already exists, update if it does
var updatedWebhooks []*storepb.WebhooksUserSetting_Webhook
webhookExists := false
for _, existing := range existingWebhooks {
if existing.Id == webhook.Id {
updatedWebhooks = append(updatedWebhooks, webhook)
webhookExists = true
} else {
updatedWebhooks = append(updatedWebhooks, existing)
}
}
// If webhook doesn't exist, add it
if !webhookExists {
updatedWebhooks = append(updatedWebhooks, webhook)
}
_, err = s.UpsertUserSetting(ctx, &storepb.UserSetting{
UserId: userID,
Key: storepb.UserSetting_WEBHOOKS,
Value: &storepb.UserSetting_Webhooks{
Webhooks: &storepb.WebhooksUserSetting{
Webhooks: updatedWebhooks,
},
},
})
return err
}
// RemoveUserWebhook removes the webhook of the user.
func (s *Store) RemoveUserWebhook(ctx context.Context, userID int32, webhookID string) error {
oldWebhooks, err := s.GetUserWebhooks(ctx, userID)
if err != nil {
return err
}
newWebhooks := make([]*storepb.WebhooksUserSetting_Webhook, 0, len(oldWebhooks))
for _, webhook := range oldWebhooks {
if webhookID != webhook.Id {
newWebhooks = append(newWebhooks, webhook)
}
}
_, err = s.UpsertUserSetting(ctx, &storepb.UserSetting{
UserId: userID,
Key: storepb.UserSetting_WEBHOOKS,
Value: &storepb.UserSetting_Webhooks{
Webhooks: &storepb.WebhooksUserSetting{
Webhooks: newWebhooks,
},
},
})
return err
}
// UpdateUserWebhook updates an existing webhook for the user.
func (s *Store) UpdateUserWebhook(ctx context.Context, userID int32, webhook *storepb.WebhooksUserSetting_Webhook) error {
webhooks, err := s.GetUserWebhooks(ctx, userID)
if err != nil {
return err
}
for i, existing := range webhooks {
if existing.Id == webhook.Id {
webhooks[i] = webhook
break
}
}
_, err = s.UpsertUserSetting(ctx, &storepb.UserSetting{
UserId: userID,
Key: storepb.UserSetting_WEBHOOKS,
Value: &storepb.UserSetting_Webhooks{
Webhooks: &storepb.WebhooksUserSetting{
Webhooks: webhooks,
},
},
})
return err
}
func convertUserSettingFromRaw(raw *UserSetting) (*storepb.UserSetting, error) {
userSetting := &storepb.UserSetting{
UserId: raw.UserID,
@ -272,6 +380,12 @@ func convertUserSettingFromRaw(raw *UserSetting) (*storepb.UserSetting, error) {
return nil, err
}
userSetting.Value = &storepb.UserSetting_General{General: generalUserSetting}
case storepb.UserSetting_WEBHOOKS:
webhooksUserSetting := &storepb.WebhooksUserSetting{}
if err := protojsonUnmarshaler.Unmarshal([]byte(raw.Value), webhooksUserSetting); err != nil {
return nil, err
}
userSetting.Value = &storepb.UserSetting_Webhooks{Webhooks: webhooksUserSetting}
default:
return nil, nil
}
@ -313,6 +427,13 @@ func convertUserSettingToRaw(userSetting *storepb.UserSetting) (*UserSetting, er
return nil, err
}
raw.Value = string(value)
case storepb.UserSetting_WEBHOOKS:
webhooksUserSetting := userSetting.GetWebhooks()
value, err := protojson.Marshal(webhooksUserSetting)
if err != nil {
return nil, err
}
raw.Value = string(value)
default:
return nil, errors.Errorf("unsupported user setting key: %v", userSetting.Key)
}

@ -1,56 +0,0 @@
package store
import (
"context"
)
type Webhook struct {
ID int32
CreatedTs int64
UpdatedTs int64
CreatorID int32
Name string
URL string
}
type FindWebhook struct {
ID *int32
CreatorID *int32
}
type UpdateWebhook struct {
ID int32
Name *string
URL *string
}
type DeleteWebhook struct {
ID int32
}
func (s *Store) CreateWebhook(ctx context.Context, create *Webhook) (*Webhook, error) {
return s.driver.CreateWebhook(ctx, create)
}
func (s *Store) ListWebhooks(ctx context.Context, find *FindWebhook) ([]*Webhook, error) {
return s.driver.ListWebhooks(ctx, find)
}
func (s *Store) GetWebhook(ctx context.Context, find *FindWebhook) (*Webhook, error) {
list, err := s.ListWebhooks(ctx, find)
if err != nil {
return nil, err
}
if len(list) == 0 {
return nil, nil
}
return list[0], nil
}
func (s *Store) UpdateWebhook(ctx context.Context, update *UpdateWebhook) (*Webhook, error) {
return s.driver.UpdateWebhook(ctx, update)
}
func (s *Store) DeleteWebhook(ctx context.Context, delete *DeleteWebhook) error {
return s.driver.DeleteWebhook(ctx, delete)
}

@ -3,6 +3,7 @@ import { XIcon } from "lucide-react";
import React, { useEffect, useState } from "react";
import { toast } from "react-hot-toast";
import { webhookServiceClient } from "@/grpcweb";
import useCurrentUser from "@/hooks/useCurrentUser";
import useLoading from "@/hooks/useLoading";
import { useTranslate } from "@/utils/i18n";
import { generateDialog } from "./Dialog";
@ -20,6 +21,7 @@ interface State {
const CreateWebhookDialog: React.FC<Props> = (props: Props) => {
const { webhookName, destroy, onConfirm } = props;
const t = useTranslate();
const currentUser = useCurrentUser();
const [state, setState] = useState({
displayName: "",
url: "",
@ -67,9 +69,15 @@ const CreateWebhookDialog: React.FC<Props> = (props: Props) => {
return;
}
if (!currentUser) {
toast.error("User not authenticated");
return;
}
try {
if (isCreating) {
await webhookServiceClient.createWebhook({
parent: currentUser.name,
webhook: {
displayName: state.displayName,
url: state.url,

@ -3,26 +3,31 @@ import { ExternalLinkIcon, TrashIcon } from "lucide-react";
import { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import { webhookServiceClient } from "@/grpcweb";
import useCurrentUser from "@/hooks/useCurrentUser";
import { Webhook } from "@/types/proto/api/v1/webhook_service";
import { useTranslate } from "@/utils/i18n";
import showCreateWebhookDialog from "../CreateWebhookDialog";
const listWebhooks = async () => {
const { webhooks } = await webhookServiceClient.listWebhooks({});
return webhooks;
};
const WebhookSection = () => {
const t = useTranslate();
const currentUser = useCurrentUser();
const [webhooks, setWebhooks] = useState<Webhook[]>([]);
const listWebhooks = async () => {
if (!currentUser) return [];
const { webhooks } = await webhookServiceClient.listWebhooks({
parent: currentUser.name,
});
return webhooks;
};
useEffect(() => {
listWebhooks().then((webhooks) => {
setWebhooks(webhooks);
});
}, []);
}, [currentUser]);
const handleCreateAccessTokenDialogConfirm = async () => {
const handleCreateWebhookDialogConfirm = async () => {
const webhooks = await listWebhooks();
setWebhooks(webhooks);
};
@ -47,7 +52,7 @@ const WebhookSection = () => {
<Button
color="primary"
onClick={() => {
showCreateWebhookDialog(handleCreateAccessTokenDialogConfirm);
showCreateWebhookDialog(handleCreateWebhookDialogConfirm);
}}
>
{t("common.create")}

@ -8,38 +8,27 @@
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { Empty } from "../../google/protobuf/empty";
import { FieldMask } from "../../google/protobuf/field_mask";
import { Timestamp } from "../../google/protobuf/timestamp";
import { State, stateFromJSON, stateToNumber } from "./common";
import { Memo } from "./memo_service";
export const protobufPackage = "memos.api.v1";
export interface Webhook {
/**
* The resource name of the webhook.
* Format: webhooks/{webhook}
* Format: users/{user}/webhooks/{webhook}
*/
name: string;
/** Required. The display name of the webhook. */
/** The display name of the webhook. */
displayName: string;
/** Required. The target URL for the webhook. */
/** The target URL for the webhook. */
url: string;
/**
* Output only. The resource name of the creator.
* Format: users/{user}
*/
creator: string;
/** The state of the webhook. */
state: State;
/** Output only. The creation timestamp. */
createTime?:
| Date
| undefined;
/** Output only. The last update timestamp. */
updateTime?: Date | undefined;
}
export interface ListWebhooksRequest {
/**
* Required. The parent resource where webhooks are listed.
* Format: users/{user}
*/
parent: string;
}
export interface ListWebhooksResponse {
@ -49,72 +38,45 @@ export interface ListWebhooksResponse {
export interface GetWebhookRequest {
/**
* Required. The resource name of the webhook.
* Format: webhooks/{webhook}
* Required. The resource name of the webhook to retrieve.
* Format: users/{user}/webhooks/{webhook}
*/
name: string;
}
export interface CreateWebhookRequest {
/**
* Required. The parent resource where this webhook will be created.
* Format: users/{user}
*/
parent: string;
/** Required. The webhook to create. */
webhook?:
| Webhook
| undefined;
/**
* Optional. The webhook ID to use for this webhook.
* If empty, a unique ID will be generated.
* Must match the pattern [a-z0-9-]+
*/
webhookId: string;
/** Optional. If set, validate the request but don't actually create the webhook. */
/** Optional. If set, validate the request, but do not actually create the webhook. */
validateOnly: boolean;
}
export interface UpdateWebhookRequest {
/** Required. The webhook to update. */
/** Required. The webhook resource which replaces the resource on the server. */
webhook?:
| Webhook
| undefined;
/** Required. The list of fields to update. */
/** Optional. The list of fields to update. */
updateMask?: string[] | undefined;
}
export interface DeleteWebhookRequest {
/**
* Required. The resource name of the webhook to delete.
* Format: webhooks/{webhook}
* Format: users/{user}/webhooks/{webhook}
*/
name: string;
}
export interface WebhookRequestPayload {
/** The target URL for the webhook request. */
url: string;
/** The type of activity that triggered this webhook. */
activityType: string;
/**
* The resource name of the creator.
* Format: users/{user}
*/
creator: string;
/** The creation timestamp of the activity. */
createTime?:
| Date
| undefined;
/** The memo that triggered this webhook (if applicable). */
memo?: Memo | undefined;
}
function createBaseWebhook(): Webhook {
return {
name: "",
displayName: "",
url: "",
creator: "",
state: State.STATE_UNSPECIFIED,
createTime: undefined,
updateTime: undefined,
};
return { name: "", displayName: "", url: "" };
}
export const Webhook: MessageFns<Webhook> = {
@ -128,18 +90,6 @@ export const Webhook: MessageFns<Webhook> = {
if (message.url !== "") {
writer.uint32(26).string(message.url);
}
if (message.creator !== "") {
writer.uint32(34).string(message.creator);
}
if (message.state !== State.STATE_UNSPECIFIED) {
writer.uint32(40).int32(stateToNumber(message.state));
}
if (message.createTime !== undefined) {
Timestamp.encode(toTimestamp(message.createTime), writer.uint32(50).fork()).join();
}
if (message.updateTime !== undefined) {
Timestamp.encode(toTimestamp(message.updateTime), writer.uint32(58).fork()).join();
}
return writer;
},
@ -174,38 +124,6 @@ export const Webhook: MessageFns<Webhook> = {
message.url = reader.string();
continue;
}
case 4: {
if (tag !== 34) {
break;
}
message.creator = reader.string();
continue;
}
case 5: {
if (tag !== 40) {
break;
}
message.state = stateFromJSON(reader.int32());
continue;
}
case 6: {
if (tag !== 50) {
break;
}
message.createTime = fromTimestamp(Timestamp.decode(reader, reader.uint32()));
continue;
}
case 7: {
if (tag !== 58) {
break;
}
message.updateTime = fromTimestamp(Timestamp.decode(reader, reader.uint32()));
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
@ -223,20 +141,19 @@ export const Webhook: MessageFns<Webhook> = {
message.name = object.name ?? "";
message.displayName = object.displayName ?? "";
message.url = object.url ?? "";
message.creator = object.creator ?? "";
message.state = object.state ?? State.STATE_UNSPECIFIED;
message.createTime = object.createTime ?? undefined;
message.updateTime = object.updateTime ?? undefined;
return message;
},
};
function createBaseListWebhooksRequest(): ListWebhooksRequest {
return {};
return { parent: "" };
}
export const ListWebhooksRequest: MessageFns<ListWebhooksRequest> = {
encode(_: ListWebhooksRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
encode(message: ListWebhooksRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.parent !== "") {
writer.uint32(10).string(message.parent);
}
return writer;
},
@ -247,6 +164,14 @@ export const ListWebhooksRequest: MessageFns<ListWebhooksRequest> = {
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break;
}
message.parent = reader.string();
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
@ -259,8 +184,9 @@ export const ListWebhooksRequest: MessageFns<ListWebhooksRequest> = {
create(base?: DeepPartial<ListWebhooksRequest>): ListWebhooksRequest {
return ListWebhooksRequest.fromPartial(base ?? {});
},
fromPartial(_: DeepPartial<ListWebhooksRequest>): ListWebhooksRequest {
fromPartial(object: DeepPartial<ListWebhooksRequest>): ListWebhooksRequest {
const message = createBaseListWebhooksRequest();
message.parent = object.parent ?? "";
return message;
},
};
@ -358,16 +284,16 @@ export const GetWebhookRequest: MessageFns<GetWebhookRequest> = {
};
function createBaseCreateWebhookRequest(): CreateWebhookRequest {
return { webhook: undefined, webhookId: "", validateOnly: false };
return { parent: "", webhook: undefined, validateOnly: false };
}
export const CreateWebhookRequest: MessageFns<CreateWebhookRequest> = {
encode(message: CreateWebhookRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.webhook !== undefined) {
Webhook.encode(message.webhook, writer.uint32(10).fork()).join();
if (message.parent !== "") {
writer.uint32(10).string(message.parent);
}
if (message.webhookId !== "") {
writer.uint32(18).string(message.webhookId);
if (message.webhook !== undefined) {
Webhook.encode(message.webhook, writer.uint32(18).fork()).join();
}
if (message.validateOnly !== false) {
writer.uint32(24).bool(message.validateOnly);
@ -387,7 +313,7 @@ export const CreateWebhookRequest: MessageFns<CreateWebhookRequest> = {
break;
}
message.webhook = Webhook.decode(reader, reader.uint32());
message.parent = reader.string();
continue;
}
case 2: {
@ -395,7 +321,7 @@ export const CreateWebhookRequest: MessageFns<CreateWebhookRequest> = {
break;
}
message.webhookId = reader.string();
message.webhook = Webhook.decode(reader, reader.uint32());
continue;
}
case 3: {
@ -420,10 +346,10 @@ export const CreateWebhookRequest: MessageFns<CreateWebhookRequest> = {
},
fromPartial(object: DeepPartial<CreateWebhookRequest>): CreateWebhookRequest {
const message = createBaseCreateWebhookRequest();
message.parent = object.parent ?? "";
message.webhook = (object.webhook !== undefined && object.webhook !== null)
? Webhook.fromPartial(object.webhook)
: undefined;
message.webhookId = object.webhookId ?? "";
message.validateOnly = object.validateOnly ?? false;
return message;
},
@ -535,106 +461,12 @@ export const DeleteWebhookRequest: MessageFns<DeleteWebhookRequest> = {
},
};
function createBaseWebhookRequestPayload(): WebhookRequestPayload {
return { url: "", activityType: "", creator: "", createTime: undefined, memo: undefined };
}
export const WebhookRequestPayload: MessageFns<WebhookRequestPayload> = {
encode(message: WebhookRequestPayload, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.url !== "") {
writer.uint32(10).string(message.url);
}
if (message.activityType !== "") {
writer.uint32(18).string(message.activityType);
}
if (message.creator !== "") {
writer.uint32(26).string(message.creator);
}
if (message.createTime !== undefined) {
Timestamp.encode(toTimestamp(message.createTime), writer.uint32(34).fork()).join();
}
if (message.memo !== undefined) {
Memo.encode(message.memo, writer.uint32(42).fork()).join();
}
return writer;
},
decode(input: BinaryReader | Uint8Array, length?: number): WebhookRequestPayload {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseWebhookRequestPayload();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break;
}
message.url = reader.string();
continue;
}
case 2: {
if (tag !== 18) {
break;
}
message.activityType = reader.string();
continue;
}
case 3: {
if (tag !== 26) {
break;
}
message.creator = reader.string();
continue;
}
case 4: {
if (tag !== 34) {
break;
}
message.createTime = fromTimestamp(Timestamp.decode(reader, reader.uint32()));
continue;
}
case 5: {
if (tag !== 42) {
break;
}
message.memo = Memo.decode(reader, reader.uint32());
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
}
return message;
},
create(base?: DeepPartial<WebhookRequestPayload>): WebhookRequestPayload {
return WebhookRequestPayload.fromPartial(base ?? {});
},
fromPartial(object: DeepPartial<WebhookRequestPayload>): WebhookRequestPayload {
const message = createBaseWebhookRequestPayload();
message.url = object.url ?? "";
message.activityType = object.activityType ?? "";
message.creator = object.creator ?? "";
message.createTime = object.createTime ?? undefined;
message.memo = (object.memo !== undefined && object.memo !== null) ? Memo.fromPartial(object.memo) : undefined;
return message;
},
};
export type WebhookServiceDefinition = typeof WebhookServiceDefinition;
export const WebhookServiceDefinition = {
name: "WebhookService",
fullName: "memos.api.v1.WebhookService",
methods: {
/** ListWebhooks returns a list of webhooks. */
/** ListWebhooks returns a list of webhooks for a user. */
listWebhooks: {
name: "ListWebhooks",
requestType: ListWebhooksRequest,
@ -643,8 +475,46 @@ export const WebhookServiceDefinition = {
responseStream: false,
options: {
_unknownFields: {
8410: [new Uint8Array([6, 112, 97, 114, 101, 110, 116])],
578365826: [
new Uint8Array([18, 18, 16, 47, 97, 112, 105, 47, 118, 49, 47, 119, 101, 98, 104, 111, 111, 107, 115]),
new Uint8Array([
35,
18,
33,
47,
97,
112,
105,
47,
118,
49,
47,
123,
112,
97,
114,
101,
110,
116,
61,
117,
115,
101,
114,
115,
47,
42,
125,
47,
119,
101,
98,
104,
111,
111,
107,
115,
]),
],
},
},
@ -661,9 +531,9 @@ export const WebhookServiceDefinition = {
8410: [new Uint8Array([4, 110, 97, 109, 101])],
578365826: [
new Uint8Array([
27,
35,
18,
25,
33,
47,
97,
112,
@ -678,6 +548,14 @@ export const WebhookServiceDefinition = {
109,
101,
61,
117,
115,
101,
114,
115,
47,
42,
47,
119,
101,
98,
@ -694,7 +572,7 @@ export const WebhookServiceDefinition = {
},
},
},
/** CreateWebhook creates a new webhook. */
/** CreateWebhook creates a new webhook for a user. */
createWebhook: {
name: "CreateWebhook",
requestType: CreateWebhookRequest,
@ -703,10 +581,10 @@ export const WebhookServiceDefinition = {
responseStream: false,
options: {
_unknownFields: {
8410: [new Uint8Array([7, 119, 101, 98, 104, 111, 111, 107])],
8410: [new Uint8Array([14, 112, 97, 114, 101, 110, 116, 44, 119, 101, 98, 104, 111, 111, 107])],
578365826: [
new Uint8Array([
27,
44,
58,
7,
119,
@ -717,7 +595,7 @@ export const WebhookServiceDefinition = {
111,
107,
34,
16,
33,
47,
97,
112,
@ -726,6 +604,23 @@ export const WebhookServiceDefinition = {
118,
49,
47,
123,
112,
97,
114,
101,
110,
116,
61,
117,
115,
101,
114,
115,
47,
42,
125,
47,
119,
101,
98,
@ -739,7 +634,7 @@ export const WebhookServiceDefinition = {
},
},
},
/** UpdateWebhook updates a webhook. */
/** UpdateWebhook updates a webhook for a user. */
updateWebhook: {
name: "UpdateWebhook",
requestType: UpdateWebhookRequest,
@ -774,7 +669,7 @@ export const WebhookServiceDefinition = {
],
578365826: [
new Uint8Array([
44,
52,
58,
7,
119,
@ -785,7 +680,7 @@ export const WebhookServiceDefinition = {
111,
107,
50,
33,
41,
47,
97,
112,
@ -808,6 +703,14 @@ export const WebhookServiceDefinition = {
109,
101,
61,
117,
115,
101,
114,
115,
47,
42,
47,
119,
101,
98,
@ -824,7 +727,7 @@ export const WebhookServiceDefinition = {
},
},
},
/** DeleteWebhook deletes a webhook. */
/** DeleteWebhook deletes a webhook for a user. */
deleteWebhook: {
name: "DeleteWebhook",
requestType: DeleteWebhookRequest,
@ -836,9 +739,9 @@ export const WebhookServiceDefinition = {
8410: [new Uint8Array([4, 110, 97, 109, 101])],
578365826: [
new Uint8Array([
27,
35,
42,
25,
33,
47,
97,
112,
@ -853,6 +756,14 @@ export const WebhookServiceDefinition = {
109,
101,
61,
117,
115,
101,
114,
115,
47,
42,
47,
119,
101,
98,
@ -880,18 +791,6 @@ export type DeepPartial<T> = T extends Builtin ? T
: T extends {} ? { [K in keyof T]?: DeepPartial<T[K]> }
: Partial<T>;
function toTimestamp(date: Date): Timestamp {
const seconds = Math.trunc(date.getTime() / 1_000);
const nanos = (date.getTime() % 1_000) * 1_000_000;
return { seconds, nanos };
}
function fromTimestamp(t: Timestamp): Date {
let millis = (t.seconds || 0) * 1_000;
millis += (t.nanos || 0) / 1_000_000;
return new globalThis.Date(millis);
}
export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T;

@ -35,7 +35,7 @@ export enum Edition {
EDITION_2024 = "EDITION_2024",
/**
* EDITION_1_TEST_ONLY - Placeholder editions for testing feature resolution. These should not be
* used or relied on outside of tests.
* used or relyed on outside of tests.
*/
EDITION_1_TEST_ONLY = "EDITION_1_TEST_ONLY",
EDITION_2_TEST_ONLY = "EDITION_2_TEST_ONLY",
@ -177,19 +177,11 @@ export interface FileDescriptorProto {
* The supported values are "proto2", "proto3", and "editions".
*
* If `edition` is present, this value must be "editions".
* WARNING: This field should only be used by protobuf plugins or special
* cases like the proto compiler. Other uses are discouraged and
* developers should rely on the protoreflect APIs for their client language.
*/
syntax?:
| string
| undefined;
/**
* The edition of the proto file.
* WARNING: This field should only be used by protobuf plugins or special
* cases like the proto compiler. Other uses are discouraged and
* developers should rely on the protoreflect APIs for their client language.
*/
/** The edition of the proto file. */
edition?: Edition | undefined;
}
@ -836,12 +828,7 @@ export interface FileOptions {
rubyPackage?:
| string
| undefined;
/**
* Any features defined in the specific edition.
* WARNING: This field should only be used by protobuf plugins or special
* cases like the proto compiler. Other uses are discouraged and
* developers should rely on the protoreflect APIs for their client language.
*/
/** Any features defined in the specific edition. */
features?:
| FeatureSet
| undefined;
@ -979,12 +966,7 @@ export interface MessageOptions {
deprecatedLegacyJsonFieldConflicts?:
| boolean
| undefined;
/**
* Any features defined in the specific edition.
* WARNING: This field should only be used by protobuf plugins or special
* cases like the proto compiler. Other uses are discouraged and
* developers should rely on the protoreflect APIs for their client language.
*/
/** Any features defined in the specific edition. */
features?:
| FeatureSet
| undefined;
@ -994,13 +976,12 @@ export interface MessageOptions {
export interface FieldOptions {
/**
* NOTE: ctype is deprecated. Use `features.(pb.cpp).string_type` instead.
* The ctype option instructs the C++ code generator to use a different
* representation of the field than it normally would. See the specific
* options below. This option is only implemented to support use of
* [ctype=CORD] and [ctype=STRING] (the default) on non-repeated fields of
* type "bytes" in the open source release.
* TODO: make ctype actually deprecated.
* type "bytes" in the open source release -- sorry, we'll try to include
* other types in a future version!
*/
ctype?:
| FieldOptions_CType
@ -1089,12 +1070,7 @@ export interface FieldOptions {
retention?: FieldOptions_OptionRetention | undefined;
targets: FieldOptions_OptionTargetType[];
editionDefaults: FieldOptions_EditionDefault[];
/**
* Any features defined in the specific edition.
* WARNING: This field should only be used by protobuf plugins or special
* cases like the proto compiler. Other uses are discouraged and
* developers should rely on the protoreflect APIs for their client language.
*/
/** Any features defined in the specific edition. */
features?: FeatureSet | undefined;
featureSupport?:
| FieldOptions_FeatureSupport
@ -1193,7 +1169,11 @@ export function fieldOptions_JSTypeToNumber(object: FieldOptions_JSType): number
}
}
/** If set to RETENTION_SOURCE, the option will be omitted from the binary. */
/**
* If set to RETENTION_SOURCE, the option will be omitted from the binary.
* Note: as of January 2023, support for this is in progress and does not yet
* have an effect (b/264593489).
*/
export enum FieldOptions_OptionRetention {
RETENTION_UNKNOWN = "RETENTION_UNKNOWN",
RETENTION_RUNTIME = "RETENTION_RUNTIME",
@ -1236,7 +1216,8 @@ export function fieldOptions_OptionRetentionToNumber(object: FieldOptions_Option
/**
* This indicates the types of entities that the field may apply to when used
* as an option. If it is unset, then the field may be freely used as an
* option on any kind of entity.
* option on any kind of entity. Note: as of January 2023, support for this is
* in progress and does not yet have an effect (b/264593489).
*/
export enum FieldOptions_OptionTargetType {
TARGET_TYPE_UNKNOWN = "TARGET_TYPE_UNKNOWN",
@ -1360,12 +1341,7 @@ export interface FieldOptions_FeatureSupport {
}
export interface OneofOptions {
/**
* Any features defined in the specific edition.
* WARNING: This field should only be used by protobuf plugins or special
* cases like the proto compiler. Other uses are discouraged and
* developers should rely on the protoreflect APIs for their client language.
*/
/** Any features defined in the specific edition. */
features?:
| FeatureSet
| undefined;
@ -1403,12 +1379,7 @@ export interface EnumOptions {
deprecatedLegacyJsonFieldConflicts?:
| boolean
| undefined;
/**
* Any features defined in the specific edition.
* WARNING: This field should only be used by protobuf plugins or special
* cases like the proto compiler. Other uses are discouraged and
* developers should rely on the protoreflect APIs for their client language.
*/
/** Any features defined in the specific edition. */
features?:
| FeatureSet
| undefined;
@ -1426,12 +1397,7 @@ export interface EnumValueOptions {
deprecated?:
| boolean
| undefined;
/**
* Any features defined in the specific edition.
* WARNING: This field should only be used by protobuf plugins or special
* cases like the proto compiler. Other uses are discouraged and
* developers should rely on the protoreflect APIs for their client language.
*/
/** Any features defined in the specific edition. */
features?:
| FeatureSet
| undefined;
@ -1452,12 +1418,7 @@ export interface EnumValueOptions {
}
export interface ServiceOptions {
/**
* Any features defined in the specific edition.
* WARNING: This field should only be used by protobuf plugins or special
* cases like the proto compiler. Other uses are discouraged and
* developers should rely on the protoreflect APIs for their client language.
*/
/** Any features defined in the specific edition. */
features?:
| FeatureSet
| undefined;
@ -1485,12 +1446,7 @@ export interface MethodOptions {
idempotencyLevel?:
| MethodOptions_IdempotencyLevel
| undefined;
/**
* Any features defined in the specific edition.
* WARNING: This field should only be used by protobuf plugins or special
* cases like the proto compiler. Other uses are discouraged and
* developers should rely on the protoreflect APIs for their client language.
*/
/** Any features defined in the specific edition. */
features?:
| FeatureSet
| undefined;
@ -1593,7 +1549,6 @@ export interface FeatureSet {
utf8Validation?: FeatureSet_Utf8Validation | undefined;
messageEncoding?: FeatureSet_MessageEncoding | undefined;
jsonFormat?: FeatureSet_JsonFormat | undefined;
enforceNamingStyle?: FeatureSet_EnforceNamingStyle | undefined;
}
export enum FeatureSet_FieldPresence {
@ -1836,45 +1791,6 @@ export function featureSet_JsonFormatToNumber(object: FeatureSet_JsonFormat): nu
}
}
export enum FeatureSet_EnforceNamingStyle {
ENFORCE_NAMING_STYLE_UNKNOWN = "ENFORCE_NAMING_STYLE_UNKNOWN",
STYLE2024 = "STYLE2024",
STYLE_LEGACY = "STYLE_LEGACY",
UNRECOGNIZED = "UNRECOGNIZED",
}
export function featureSet_EnforceNamingStyleFromJSON(object: any): FeatureSet_EnforceNamingStyle {
switch (object) {
case 0:
case "ENFORCE_NAMING_STYLE_UNKNOWN":
return FeatureSet_EnforceNamingStyle.ENFORCE_NAMING_STYLE_UNKNOWN;
case 1:
case "STYLE2024":
return FeatureSet_EnforceNamingStyle.STYLE2024;
case 2:
case "STYLE_LEGACY":
return FeatureSet_EnforceNamingStyle.STYLE_LEGACY;
case -1:
case "UNRECOGNIZED":
default:
return FeatureSet_EnforceNamingStyle.UNRECOGNIZED;
}
}
export function featureSet_EnforceNamingStyleToNumber(object: FeatureSet_EnforceNamingStyle): number {
switch (object) {
case FeatureSet_EnforceNamingStyle.ENFORCE_NAMING_STYLE_UNKNOWN:
return 0;
case FeatureSet_EnforceNamingStyle.STYLE2024:
return 1;
case FeatureSet_EnforceNamingStyle.STYLE_LEGACY:
return 2;
case FeatureSet_EnforceNamingStyle.UNRECOGNIZED:
default:
return -1;
}
}
/**
* A compiled specification for the defaults of a set of features. These
* messages are generated from FeatureSet extensions and can be used to seed
@ -4998,7 +4914,6 @@ function createBaseFeatureSet(): FeatureSet {
utf8Validation: FeatureSet_Utf8Validation.UTF8_VALIDATION_UNKNOWN,
messageEncoding: FeatureSet_MessageEncoding.MESSAGE_ENCODING_UNKNOWN,
jsonFormat: FeatureSet_JsonFormat.JSON_FORMAT_UNKNOWN,
enforceNamingStyle: FeatureSet_EnforceNamingStyle.ENFORCE_NAMING_STYLE_UNKNOWN,
};
}
@ -5033,12 +4948,6 @@ export const FeatureSet: MessageFns<FeatureSet> = {
if (message.jsonFormat !== undefined && message.jsonFormat !== FeatureSet_JsonFormat.JSON_FORMAT_UNKNOWN) {
writer.uint32(48).int32(featureSet_JsonFormatToNumber(message.jsonFormat));
}
if (
message.enforceNamingStyle !== undefined &&
message.enforceNamingStyle !== FeatureSet_EnforceNamingStyle.ENFORCE_NAMING_STYLE_UNKNOWN
) {
writer.uint32(56).int32(featureSet_EnforceNamingStyleToNumber(message.enforceNamingStyle));
}
return writer;
},
@ -5097,14 +5006,6 @@ export const FeatureSet: MessageFns<FeatureSet> = {
message.jsonFormat = featureSet_JsonFormatFromJSON(reader.int32());
continue;
}
case 7: {
if (tag !== 56) {
break;
}
message.enforceNamingStyle = featureSet_EnforceNamingStyleFromJSON(reader.int32());
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
@ -5126,8 +5027,6 @@ export const FeatureSet: MessageFns<FeatureSet> = {
message.utf8Validation = object.utf8Validation ?? FeatureSet_Utf8Validation.UTF8_VALIDATION_UNKNOWN;
message.messageEncoding = object.messageEncoding ?? FeatureSet_MessageEncoding.MESSAGE_ENCODING_UNKNOWN;
message.jsonFormat = object.jsonFormat ?? FeatureSet_JsonFormat.JSON_FORMAT_UNKNOWN;
message.enforceNamingStyle = object.enforceNamingStyle ??
FeatureSet_EnforceNamingStyle.ENFORCE_NAMING_STYLE_UNKNOWN;
return message;
},
};

Loading…
Cancel
Save