Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
canifa_note
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
canifa_note
Commits
34c26a39
Commit
34c26a39
authored
Jan 11, 2025
by
johnnyjoy
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: implement user stats endpoint
parent
5ff8ab9a
Changes
8
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
843 additions
and
249 deletions
+843
-249
user_service.proto
proto/api/v1/user_service.proto
+39
-0
user_service.pb.go
proto/gen/api/v1/user_service.pb.go
+525
-245
user_service.pb.gw.go
proto/gen/api/v1/user_service.pb.gw.go
+75
-0
user_service_grpc.pb.go
proto/gen/api/v1/user_service_grpc.pb.go
+38
-0
apidocs.swagger.yaml
proto/gen/apidocs.swagger.yaml
+70
-0
acl_config.go
server/router/api/v1/acl_config.go
+1
-0
user_service.go
server/router/api/v1/user_service.go
+93
-0
memo_test.go
test/store/memo_test.go
+2
-4
No files found.
proto/api/v1/user_service.proto
View file @
34c26a39
...
...
@@ -53,6 +53,12 @@ service UserService {
option
(
google.api.http
)
=
{
delete
:
"/api/v1/{name=users/*}"
};
option
(
google.api.method_signature
)
=
"name"
;
}
// ListUserStats returns the stats of a user.
// Use `users/-` to list all users.
rpc
ListUserStats
(
ListUserStatsRequest
)
returns
(
ListUserStatsResponse
)
{
option
(
google.api.http
)
=
{
get
:
"/api/v1/{name=users/*}/stats"
};
option
(
google.api.method_signature
)
=
"name"
;
}
// GetUserSetting gets the setting of a user.
rpc
GetUserSetting
(
GetUserSettingRequest
)
returns
(
UserSetting
)
{
option
(
google.api.http
)
=
{
get
:
"/api/v1/{name=users/*}/setting"
};
...
...
@@ -165,6 +171,39 @@ message DeleteUserRequest {
string
name
=
1
;
}
message
UserStats
{
// The name of the user.
// Format: users/{user}
string
name
=
1
;
// The timestamps when the memos were displayed.
// We should return raw data to the client, and let the client format the data with the user's timezone.
repeated
google.protobuf.Timestamp
memo_display_timestamps
=
2
;
// The stats of memo types.
MemoTypeStats
memo_type_stats
=
3
;
// The count of tags.
// Format: "tag1": 1, "tag2": 2
map
<
string
,
int32
>
tag_count
=
4
;
message
MemoTypeStats
{
int32
link_count
=
1
;
int32
task_count
=
2
;
int32
code_count
=
3
;
}
}
message
ListUserStatsRequest
{
// The name of the user.
// Format: users/{user}. Use "-" to list all users.
string
name
=
1
;
}
message
ListUserStatsResponse
{
repeated
UserStats
user_stats
=
1
;
}
message
UserSetting
{
// The name of the user.
// Format: users/{user}
...
...
proto/gen/api/v1/user_service.pb.go
View file @
34c26a39
This diff is collapsed.
Click to expand it.
proto/gen/api/v1/user_service.pb.gw.go
View file @
34c26a39
...
...
@@ -309,6 +309,42 @@ func local_request_UserService_DeleteUser_0(ctx context.Context, marshaler runti
return
msg
,
metadata
,
err
}
func
request_UserService_ListUserStats_0
(
ctx
context
.
Context
,
marshaler
runtime
.
Marshaler
,
client
UserServiceClient
,
req
*
http
.
Request
,
pathParams
map
[
string
]
string
)
(
proto
.
Message
,
runtime
.
ServerMetadata
,
error
)
{
var
(
protoReq
ListUserStatsRequest
metadata
runtime
.
ServerMetadata
err
error
)
val
,
ok
:=
pathParams
[
"name"
]
if
!
ok
{
return
nil
,
metadata
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"missing parameter %s"
,
"name"
)
}
protoReq
.
Name
,
err
=
runtime
.
String
(
val
)
if
err
!=
nil
{
return
nil
,
metadata
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"type mismatch, parameter: %s, error: %v"
,
"name"
,
err
)
}
msg
,
err
:=
client
.
ListUserStats
(
ctx
,
&
protoReq
,
grpc
.
Header
(
&
metadata
.
HeaderMD
),
grpc
.
Trailer
(
&
metadata
.
TrailerMD
))
return
msg
,
metadata
,
err
}
func
local_request_UserService_ListUserStats_0
(
ctx
context
.
Context
,
marshaler
runtime
.
Marshaler
,
server
UserServiceServer
,
req
*
http
.
Request
,
pathParams
map
[
string
]
string
)
(
proto
.
Message
,
runtime
.
ServerMetadata
,
error
)
{
var
(
protoReq
ListUserStatsRequest
metadata
runtime
.
ServerMetadata
err
error
)
val
,
ok
:=
pathParams
[
"name"
]
if
!
ok
{
return
nil
,
metadata
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"missing parameter %s"
,
"name"
)
}
protoReq
.
Name
,
err
=
runtime
.
String
(
val
)
if
err
!=
nil
{
return
nil
,
metadata
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"type mismatch, parameter: %s, error: %v"
,
"name"
,
err
)
}
msg
,
err
:=
server
.
ListUserStats
(
ctx
,
&
protoReq
)
return
msg
,
metadata
,
err
}
func
request_UserService_GetUserSetting_0
(
ctx
context
.
Context
,
marshaler
runtime
.
Marshaler
,
client
UserServiceClient
,
req
*
http
.
Request
,
pathParams
map
[
string
]
string
)
(
proto
.
Message
,
runtime
.
ServerMetadata
,
error
)
{
var
(
protoReq
GetUserSettingRequest
...
...
@@ -699,6 +735,26 @@ func RegisterUserServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
}
forward_UserService_DeleteUser_0
(
annotatedContext
,
mux
,
outboundMarshaler
,
w
,
req
,
resp
,
mux
.
GetForwardResponseOptions
()
...
)
})
mux
.
Handle
(
http
.
MethodGet
,
pattern_UserService_ListUserStats_0
,
func
(
w
http
.
ResponseWriter
,
req
*
http
.
Request
,
pathParams
map
[
string
]
string
)
{
ctx
,
cancel
:=
context
.
WithCancel
(
req
.
Context
())
defer
cancel
()
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.UserService/ListUserStats"
,
runtime
.
WithHTTPPathPattern
(
"/api/v1/{name=users/*}/stats"
))
if
err
!=
nil
{
runtime
.
HTTPError
(
ctx
,
mux
,
outboundMarshaler
,
w
,
req
,
err
)
return
}
resp
,
md
,
err
:=
local_request_UserService_ListUserStats_0
(
annotatedContext
,
inboundMarshaler
,
server
,
req
,
pathParams
)
md
.
HeaderMD
,
md
.
TrailerMD
=
metadata
.
Join
(
md
.
HeaderMD
,
stream
.
Header
()),
metadata
.
Join
(
md
.
TrailerMD
,
stream
.
Trailer
())
annotatedContext
=
runtime
.
NewServerMetadataContext
(
annotatedContext
,
md
)
if
err
!=
nil
{
runtime
.
HTTPError
(
annotatedContext
,
mux
,
outboundMarshaler
,
w
,
req
,
err
)
return
}
forward_UserService_ListUserStats_0
(
annotatedContext
,
mux
,
outboundMarshaler
,
w
,
req
,
resp
,
mux
.
GetForwardResponseOptions
()
...
)
})
mux
.
Handle
(
http
.
MethodGet
,
pattern_UserService_GetUserSetting_0
,
func
(
w
http
.
ResponseWriter
,
req
*
http
.
Request
,
pathParams
map
[
string
]
string
)
{
ctx
,
cancel
:=
context
.
WithCancel
(
req
.
Context
())
defer
cancel
()
...
...
@@ -958,6 +1014,23 @@ func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux
}
forward_UserService_DeleteUser_0
(
annotatedContext
,
mux
,
outboundMarshaler
,
w
,
req
,
resp
,
mux
.
GetForwardResponseOptions
()
...
)
})
mux
.
Handle
(
http
.
MethodGet
,
pattern_UserService_ListUserStats_0
,
func
(
w
http
.
ResponseWriter
,
req
*
http
.
Request
,
pathParams
map
[
string
]
string
)
{
ctx
,
cancel
:=
context
.
WithCancel
(
req
.
Context
())
defer
cancel
()
inboundMarshaler
,
outboundMarshaler
:=
runtime
.
MarshalerForRequest
(
mux
,
req
)
annotatedContext
,
err
:=
runtime
.
AnnotateContext
(
ctx
,
mux
,
req
,
"/memos.api.v1.UserService/ListUserStats"
,
runtime
.
WithHTTPPathPattern
(
"/api/v1/{name=users/*}/stats"
))
if
err
!=
nil
{
runtime
.
HTTPError
(
ctx
,
mux
,
outboundMarshaler
,
w
,
req
,
err
)
return
}
resp
,
md
,
err
:=
request_UserService_ListUserStats_0
(
annotatedContext
,
inboundMarshaler
,
client
,
req
,
pathParams
)
annotatedContext
=
runtime
.
NewServerMetadataContext
(
annotatedContext
,
md
)
if
err
!=
nil
{
runtime
.
HTTPError
(
annotatedContext
,
mux
,
outboundMarshaler
,
w
,
req
,
err
)
return
}
forward_UserService_ListUserStats_0
(
annotatedContext
,
mux
,
outboundMarshaler
,
w
,
req
,
resp
,
mux
.
GetForwardResponseOptions
()
...
)
})
mux
.
Handle
(
http
.
MethodGet
,
pattern_UserService_GetUserSetting_0
,
func
(
w
http
.
ResponseWriter
,
req
*
http
.
Request
,
pathParams
map
[
string
]
string
)
{
ctx
,
cancel
:=
context
.
WithCancel
(
req
.
Context
())
defer
cancel
()
...
...
@@ -1054,6 +1127,7 @@ var (
pattern_UserService_CreateUser_0
=
runtime
.
MustPattern
(
runtime
.
NewPattern
(
1
,
[]
int
{
2
,
0
,
2
,
1
,
2
,
2
},
[]
string
{
"api"
,
"v1"
,
"users"
},
""
))
pattern_UserService_UpdateUser_0
=
runtime
.
MustPattern
(
runtime
.
NewPattern
(
1
,
[]
int
{
2
,
0
,
2
,
1
,
2
,
2
,
1
,
0
,
4
,
2
,
5
,
3
},
[]
string
{
"api"
,
"v1"
,
"users"
,
"user.name"
},
""
))
pattern_UserService_DeleteUser_0
=
runtime
.
MustPattern
(
runtime
.
NewPattern
(
1
,
[]
int
{
2
,
0
,
2
,
1
,
2
,
2
,
1
,
0
,
4
,
2
,
5
,
3
},
[]
string
{
"api"
,
"v1"
,
"users"
,
"name"
},
""
))
pattern_UserService_ListUserStats_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"
,
"name"
,
"stats"
},
""
))
pattern_UserService_GetUserSetting_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"
,
"name"
,
"setting"
},
""
))
pattern_UserService_UpdateUserSetting_0
=
runtime
.
MustPattern
(
runtime
.
NewPattern
(
1
,
[]
int
{
2
,
0
,
2
,
1
,
2
,
2
,
1
,
0
,
2
,
3
,
4
,
3
,
5
,
4
},
[]
string
{
"api"
,
"v1"
,
"users"
,
"setting"
,
"setting.name"
},
""
))
pattern_UserService_ListUserAccessTokens_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"
,
"name"
,
"access_tokens"
},
""
))
...
...
@@ -1069,6 +1143,7 @@ var (
forward_UserService_CreateUser_0
=
runtime
.
ForwardResponseMessage
forward_UserService_UpdateUser_0
=
runtime
.
ForwardResponseMessage
forward_UserService_DeleteUser_0
=
runtime
.
ForwardResponseMessage
forward_UserService_ListUserStats_0
=
runtime
.
ForwardResponseMessage
forward_UserService_GetUserSetting_0
=
runtime
.
ForwardResponseMessage
forward_UserService_UpdateUserSetting_0
=
runtime
.
ForwardResponseMessage
forward_UserService_ListUserAccessTokens_0
=
runtime
.
ForwardResponseMessage
...
...
proto/gen/api/v1/user_service_grpc.pb.go
View file @
34c26a39
...
...
@@ -28,6 +28,7 @@ const (
UserService_CreateUser_FullMethodName
=
"/memos.api.v1.UserService/CreateUser"
UserService_UpdateUser_FullMethodName
=
"/memos.api.v1.UserService/UpdateUser"
UserService_DeleteUser_FullMethodName
=
"/memos.api.v1.UserService/DeleteUser"
UserService_ListUserStats_FullMethodName
=
"/memos.api.v1.UserService/ListUserStats"
UserService_GetUserSetting_FullMethodName
=
"/memos.api.v1.UserService/GetUserSetting"
UserService_UpdateUserSetting_FullMethodName
=
"/memos.api.v1.UserService/UpdateUserSetting"
UserService_ListUserAccessTokens_FullMethodName
=
"/memos.api.v1.UserService/ListUserAccessTokens"
...
...
@@ -53,6 +54,7 @@ type UserServiceClient interface {
UpdateUser
(
ctx
context
.
Context
,
in
*
UpdateUserRequest
,
opts
...
grpc
.
CallOption
)
(
*
User
,
error
)
// DeleteUser deletes a user.
DeleteUser
(
ctx
context
.
Context
,
in
*
DeleteUserRequest
,
opts
...
grpc
.
CallOption
)
(
*
emptypb
.
Empty
,
error
)
ListUserStats
(
ctx
context
.
Context
,
in
*
ListUserStatsRequest
,
opts
...
grpc
.
CallOption
)
(
*
ListUserStatsResponse
,
error
)
// GetUserSetting gets the setting of a user.
GetUserSetting
(
ctx
context
.
Context
,
in
*
GetUserSettingRequest
,
opts
...
grpc
.
CallOption
)
(
*
UserSetting
,
error
)
// UpdateUserSetting updates the setting of a user.
...
...
@@ -143,6 +145,16 @@ func (c *userServiceClient) DeleteUser(ctx context.Context, in *DeleteUserReques
return
out
,
nil
}
func
(
c
*
userServiceClient
)
ListUserStats
(
ctx
context
.
Context
,
in
*
ListUserStatsRequest
,
opts
...
grpc
.
CallOption
)
(
*
ListUserStatsResponse
,
error
)
{
cOpts
:=
append
([]
grpc
.
CallOption
{
grpc
.
StaticMethod
()},
opts
...
)
out
:=
new
(
ListUserStatsResponse
)
err
:=
c
.
cc
.
Invoke
(
ctx
,
UserService_ListUserStats_FullMethodName
,
in
,
out
,
cOpts
...
)
if
err
!=
nil
{
return
nil
,
err
}
return
out
,
nil
}
func
(
c
*
userServiceClient
)
GetUserSetting
(
ctx
context
.
Context
,
in
*
GetUserSettingRequest
,
opts
...
grpc
.
CallOption
)
(
*
UserSetting
,
error
)
{
cOpts
:=
append
([]
grpc
.
CallOption
{
grpc
.
StaticMethod
()},
opts
...
)
out
:=
new
(
UserSetting
)
...
...
@@ -211,6 +223,7 @@ type UserServiceServer interface {
UpdateUser
(
context
.
Context
,
*
UpdateUserRequest
)
(
*
User
,
error
)
// DeleteUser deletes a user.
DeleteUser
(
context
.
Context
,
*
DeleteUserRequest
)
(
*
emptypb
.
Empty
,
error
)
ListUserStats
(
context
.
Context
,
*
ListUserStatsRequest
)
(
*
ListUserStatsResponse
,
error
)
// GetUserSetting gets the setting of a user.
GetUserSetting
(
context
.
Context
,
*
GetUserSettingRequest
)
(
*
UserSetting
,
error
)
// UpdateUserSetting updates the setting of a user.
...
...
@@ -252,6 +265,9 @@ func (UnimplementedUserServiceServer) UpdateUser(context.Context, *UpdateUserReq
func
(
UnimplementedUserServiceServer
)
DeleteUser
(
context
.
Context
,
*
DeleteUserRequest
)
(
*
emptypb
.
Empty
,
error
)
{
return
nil
,
status
.
Errorf
(
codes
.
Unimplemented
,
"method DeleteUser not implemented"
)
}
func
(
UnimplementedUserServiceServer
)
ListUserStats
(
context
.
Context
,
*
ListUserStatsRequest
)
(
*
ListUserStatsResponse
,
error
)
{
return
nil
,
status
.
Errorf
(
codes
.
Unimplemented
,
"method ListUserStats not implemented"
)
}
func
(
UnimplementedUserServiceServer
)
GetUserSetting
(
context
.
Context
,
*
GetUserSettingRequest
)
(
*
UserSetting
,
error
)
{
return
nil
,
status
.
Errorf
(
codes
.
Unimplemented
,
"method GetUserSetting not implemented"
)
}
...
...
@@ -414,6 +430,24 @@ func _UserService_DeleteUser_Handler(srv interface{}, ctx context.Context, dec f
return
interceptor
(
ctx
,
in
,
info
,
handler
)
}
func
_UserService_ListUserStats_Handler
(
srv
interface
{},
ctx
context
.
Context
,
dec
func
(
interface
{})
error
,
interceptor
grpc
.
UnaryServerInterceptor
)
(
interface
{},
error
)
{
in
:=
new
(
ListUserStatsRequest
)
if
err
:=
dec
(
in
);
err
!=
nil
{
return
nil
,
err
}
if
interceptor
==
nil
{
return
srv
.
(
UserServiceServer
)
.
ListUserStats
(
ctx
,
in
)
}
info
:=
&
grpc
.
UnaryServerInfo
{
Server
:
srv
,
FullMethod
:
UserService_ListUserStats_FullMethodName
,
}
handler
:=
func
(
ctx
context
.
Context
,
req
interface
{})
(
interface
{},
error
)
{
return
srv
.
(
UserServiceServer
)
.
ListUserStats
(
ctx
,
req
.
(
*
ListUserStatsRequest
))
}
return
interceptor
(
ctx
,
in
,
info
,
handler
)
}
func
_UserService_GetUserSetting_Handler
(
srv
interface
{},
ctx
context
.
Context
,
dec
func
(
interface
{})
error
,
interceptor
grpc
.
UnaryServerInterceptor
)
(
interface
{},
error
)
{
in
:=
new
(
GetUserSettingRequest
)
if
err
:=
dec
(
in
);
err
!=
nil
{
...
...
@@ -539,6 +573,10 @@ var UserService_ServiceDesc = grpc.ServiceDesc{
MethodName
:
"DeleteUser"
,
Handler
:
_UserService_DeleteUser_Handler
,
},
{
MethodName
:
"ListUserStats"
,
Handler
:
_UserService_ListUserStats_Handler
,
},
{
MethodName
:
"GetUserSetting"
,
Handler
:
_UserService_GetUserSetting_Handler
,
...
...
proto/gen/apidocs.swagger.yaml
View file @
34c26a39
...
...
@@ -1464,6 +1464,29 @@ paths:
pattern: users/[^/]+
tags
:
-
UserService
/api/v1/{name}/stats
:
get
:
operationId
:
UserService_ListUserStats
responses
:
"
200"
:
description
:
A successful response.
schema
:
$ref
:
'
#/definitions/v1ListUserStatsResponse'
default
:
description
:
An unexpected error response.
schema
:
$ref
:
'
#/definitions/googlerpcStatus'
parameters
:
-
name
:
name
description
:
|-
The name of the user.
Format: users/{user}. Use "-" to list all users.
in: path
required: true
type: string
pattern: users/[^/]+
tags
:
-
UserService
/api/v1/{parent}/tags/{tag}
:
delete
:
summary
:
DeleteMemoTag deletes a tag for a memo.
...
...
@@ -1808,6 +1831,18 @@ definitions:
expiresAt
:
type
:
string
format
:
date-time
UserStatsMemoTypeStats
:
type
:
object
properties
:
linkCount
:
type
:
integer
format
:
int32
taskCount
:
type
:
integer
format
:
int32
codeCount
:
type
:
integer
format
:
int32
WorkspaceStorageSettingS3Config
:
type
:
object
properties
:
...
...
@@ -2635,6 +2670,14 @@ definitions:
items
:
type
:
object
$ref
:
'
#/definitions/v1UserAccessToken'
v1ListUserStatsResponse
:
type
:
object
properties
:
userStats
:
type
:
array
items
:
type
:
object
$ref
:
'
#/definitions/v1UserStats'
v1ListUsersResponse
:
type
:
object
properties
:
...
...
@@ -3074,6 +3117,33 @@ definitions:
expiresAt
:
type
:
string
format
:
date-time
v1UserStats
:
type
:
object
properties
:
name
:
type
:
string
title
:
|-
The name of the user.
Format: users/{user}
memoDisplayTimestamps
:
type
:
array
items
:
type
:
string
format
:
date-time
description
:
|-
The timestamps when the memos were displayed.
We should return raw data to the client, and let the client format the data with the user's timezone.
memoTypeStats
:
$ref
:
'
#/definitions/UserStatsMemoTypeStats'
description
:
The stats of memo types.
tagCount
:
type
:
object
additionalProperties
:
type
:
integer
format
:
int32
title
:
|-
The count of tags.
Format: "tag1": 1, "tag2": 2
v1Visibility
:
type
:
string
enum
:
...
...
server/router/api/v1/acl_config.go
View file @
34c26a39
...
...
@@ -13,6 +13,7 @@ var authenticationAllowlistMethods = map[string]bool{
"/memos.api.v1.AuthService/SignUp"
:
true
,
"/memos.api.v1.UserService/GetUser"
:
true
,
"/memos.api.v1.UserService/GetUserAvatarBinary"
:
true
,
"/memos.api.v1.UserService/ListUserStats"
:
true
,
"/memos.api.v1.UserService/SearchUsers"
:
true
,
"/memos.api.v1.MemoService/GetMemo"
:
true
,
"/memos.api.v1.MemoService/GetMemoByUid"
:
true
,
...
...
server/router/api/v1/user_service.go
View file @
34c26a39
...
...
@@ -275,6 +275,99 @@ func (s *APIV1Service) DeleteUser(ctx context.Context, request *v1pb.DeleteUserR
return
&
emptypb
.
Empty
{},
nil
}
func
(
s
*
APIV1Service
)
ListUserStats
(
ctx
context
.
Context
,
request
*
v1pb
.
ListUserStatsRequest
)
(
*
v1pb
.
ListUserStatsResponse
,
error
)
{
currentUser
,
err
:=
s
.
GetCurrentUser
(
ctx
)
if
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
Internal
,
"failed to get user: %v"
,
err
)
}
// For unauthenticated users, only public memos are visible.
defaultVisibilities
:=
[]
store
.
Visibility
{
store
.
Public
}
if
currentUser
!=
nil
{
// For authenticated users, protected memos are also visible.
defaultVisibilities
=
append
(
defaultVisibilities
,
store
.
Protected
)
}
users
:=
[]
*
store
.
User
{}
if
request
.
Name
==
"users/-"
{
users
,
err
=
s
.
Store
.
ListUsers
(
ctx
,
&
store
.
FindUser
{})
if
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
Internal
,
"failed to list users: %v"
,
err
)
}
}
else
{
userID
,
err
:=
ExtractUserIDFromName
(
request
.
Name
)
if
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"invalid user name: %v"
,
err
)
}
if
userID
==
currentUser
.
ID
{
users
=
append
(
users
,
currentUser
)
}
else
{
user
,
err
:=
s
.
Store
.
GetUser
(
ctx
,
&
store
.
FindUser
{
ID
:
&
userID
})
if
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
Internal
,
"failed to get user: %v"
,
err
)
}
if
user
==
nil
{
return
nil
,
status
.
Errorf
(
codes
.
NotFound
,
"user not found"
)
}
users
=
append
(
users
,
user
)
}
}
workspaceMemoRelatedSetting
,
err
:=
s
.
Store
.
GetWorkspaceMemoRelatedSetting
(
ctx
)
if
err
!=
nil
{
return
nil
,
errors
.
Wrap
(
err
,
"failed to get workspace memo related setting"
)
}
userStatsList
:=
[]
*
v1pb
.
UserStats
{}
for
_
,
user
:=
range
users
{
userStats
:=
&
v1pb
.
UserStats
{
Name
:
fmt
.
Sprintf
(
"%s%d"
,
UserNamePrefix
,
user
.
ID
),
MemoDisplayTimestamps
:
[]
*
timestamppb
.
Timestamp
{},
MemoTypeStats
:
&
v1pb
.
UserStats_MemoTypeStats
{},
TagCount
:
map
[
string
]
int32
{},
}
var
visibilities
[]
store
.
Visibility
=
defaultVisibilities
// For the current user, show all memos including private ones.
if
user
.
ID
==
currentUser
.
ID
{
visibilities
=
[]
store
.
Visibility
{
store
.
Public
,
store
.
Protected
,
store
.
Private
}
}
memos
,
err
:=
s
.
Store
.
ListMemos
(
ctx
,
&
store
.
FindMemo
{
// Exclude comments by default.
ExcludeComments
:
true
,
ExcludeContent
:
true
,
CreatorID
:
&
user
.
ID
,
VisibilityList
:
visibilities
,
})
if
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
Internal
,
"failed to list memos: %v"
,
err
)
}
for
_
,
memo
:=
range
memos
{
displayTs
:=
memo
.
CreatedTs
if
workspaceMemoRelatedSetting
.
DisplayWithUpdateTime
{
displayTs
=
memo
.
UpdatedTs
}
userStats
.
MemoDisplayTimestamps
=
append
(
userStats
.
MemoDisplayTimestamps
,
timestamppb
.
New
(
time
.
Unix
(
displayTs
,
0
)))
// Handle duplicated tags.
for
_
,
tag
:=
range
memo
.
Payload
.
Tags
{
userStats
.
TagCount
[
tag
]
++
}
if
memo
.
Payload
.
Property
.
GetHasLink
()
{
userStats
.
MemoTypeStats
.
LinkCount
++
}
if
memo
.
Payload
.
Property
.
GetHasTaskList
()
{
userStats
.
MemoTypeStats
.
TaskCount
++
}
if
memo
.
Payload
.
Property
.
GetHasCode
()
{
userStats
.
MemoTypeStats
.
CodeCount
++
}
}
userStatsList
=
append
(
userStatsList
,
userStats
)
}
return
&
v1pb
.
ListUserStatsResponse
{
UserStats
:
userStatsList
,
},
nil
}
func
getDefaultUserSetting
(
workspaceMemoRelatedSetting
*
storepb
.
WorkspaceMemoRelatedSetting
)
*
v1pb
.
UserSetting
{
defaultVisibility
:=
"PRIVATE"
if
workspaceMemoRelatedSetting
.
DefaultVisibility
!=
""
{
...
...
test/store/memo_test.go
View file @
34c26a39
...
...
@@ -54,10 +54,8 @@ func TestMemoStore(t *testing.T) {
require
.
Equal
(
t
,
0
,
len
(
memoList
))
memoList
,
err
=
ts
.
ListMemos
(
ctx
,
&
store
.
FindMemo
{
CreatorID
:
&
user
.
ID
,
VisibilityList
:
[]
store
.
Visibility
{
store
.
Public
,
},
CreatorID
:
&
user
.
ID
,
VisibilityList
:
[]
store
.
Visibility
{
store
.
Public
},
})
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
0
,
len
(
memoList
))
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment