GraphQL (part 2): Model changes and resolvers (#19486)
This PR adds the necessary resolvers to support the graphQL front-end. Some additional methods need to be added to the model structs to support this. Comments have been made to clarify their purpose. There is a slight duplication of code in the api layer. But eventually that's going to go away when we move away from the REST API entirely. The schema is in api4/schema.graphqls and gets compiled into the binary as an asset. The GraphiQL editor url is at GET /api/v5/graphql. Everything is behind the feature flag MM_FEATUREFLAGS_GRAPHQL. ```release-note NONE ```
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
88968f9e17
Коммит
3f52bd197a
@@ -6,6 +6,8 @@ package model
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"sort"
|
||||
@@ -159,6 +161,23 @@ func WithID(ID string) ChannelOption {
|
||||
}
|
||||
}
|
||||
|
||||
// The following are some GraphQL methods necessary to return the
|
||||
// data in float64 type. The spec doesn't support 64 bit integers,
|
||||
// so we have to pass the data in float64. The _ at the end is
|
||||
// a hack to keep the attribute name same in GraphQL schema.
|
||||
|
||||
func (o *Channel) CreateAt_() float64 {
|
||||
return float64(o.CreateAt)
|
||||
}
|
||||
|
||||
func (o *Channel) UpdateAt_() float64 {
|
||||
return float64(o.UpdateAt)
|
||||
}
|
||||
|
||||
func (o *Channel) DeleteAt_() float64 {
|
||||
return float64(o.DeleteAt)
|
||||
}
|
||||
|
||||
func (o *Channel) DeepCopy() *Channel {
|
||||
copy := *o
|
||||
if copy.SchemeId != nil {
|
||||
@@ -305,6 +324,24 @@ func (o *Channel) GetOtherUserIdForDM(userId string) string {
|
||||
return otherUserId
|
||||
}
|
||||
|
||||
func (ChannelType) ImplementsGraphQLType(name string) bool {
|
||||
return name == "ChannelType"
|
||||
}
|
||||
|
||||
func (t ChannelType) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(string(t))
|
||||
}
|
||||
|
||||
func (t *ChannelType) UnmarshalGraphQL(input interface{}) error {
|
||||
chType, ok := input.(string)
|
||||
if !ok {
|
||||
return errors.New("wrong type")
|
||||
}
|
||||
|
||||
*t = ChannelType(chType)
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetDMNameFromIds(userId1, userId2 string) string {
|
||||
if userId1 > userId2 {
|
||||
return userId2 + "__" + userId1
|
||||
|
||||
@@ -60,6 +60,31 @@ type ChannelMember struct {
|
||||
ExplicitRoles string `json:"explicit_roles"`
|
||||
}
|
||||
|
||||
// The following are some GraphQL methods necessary to return the
|
||||
// data in float64 type. The spec doesn't support 64 bit integers,
|
||||
// so we have to pass the data in float64. The _ at the end is
|
||||
// a hack to keep the attribute name same in GraphQL schema.
|
||||
|
||||
func (o *ChannelMember) LastViewedAt_() float64 {
|
||||
return float64(o.LastViewedAt)
|
||||
}
|
||||
|
||||
func (o *ChannelMember) MsgCount_() float64 {
|
||||
return float64(o.MsgCount)
|
||||
}
|
||||
|
||||
func (o *ChannelMember) MentionCount_() float64 {
|
||||
return float64(o.MentionCount)
|
||||
}
|
||||
|
||||
func (o *ChannelMember) MentionCountRoot_() float64 {
|
||||
return float64(o.MentionCountRoot)
|
||||
}
|
||||
|
||||
func (o *ChannelMember) LastUpdateAt_() float64 {
|
||||
return float64(o.LastUpdateAt)
|
||||
}
|
||||
|
||||
// ChannelMemberWithTeamData contains ChannelMember appended with extra team information
|
||||
// as well.
|
||||
type ChannelMemberWithTeamData struct {
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
@@ -54,6 +56,10 @@ type SidebarCategoryWithChannels struct {
|
||||
Channels []string `json:"channel_ids"`
|
||||
}
|
||||
|
||||
func (sc SidebarCategoryWithChannels) ChannelIds() []string {
|
||||
return sc.Channels
|
||||
}
|
||||
|
||||
type SidebarCategoryOrder []string
|
||||
|
||||
// OrderedSidebarCategories combines categories, their channel IDs and an array of Category IDs, sorted
|
||||
@@ -83,3 +89,39 @@ func IsValidCategoryId(s string) bool {
|
||||
// Or default categories can follow the pattern {type}_{userID}_{teamID}
|
||||
return categoryIdPattern.MatchString(s)
|
||||
}
|
||||
|
||||
func (SidebarCategoryType) ImplementsGraphQLType(name string) bool {
|
||||
return name == "SidebarCategoryType"
|
||||
}
|
||||
|
||||
func (t SidebarCategoryType) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(string(t))
|
||||
}
|
||||
|
||||
func (t *SidebarCategoryType) UnmarshalGraphQL(input interface{}) error {
|
||||
chType, ok := input.(string)
|
||||
if !ok {
|
||||
return errors.New("wrong type")
|
||||
}
|
||||
|
||||
*t = SidebarCategoryType(chType)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (SidebarCategorySorting) ImplementsGraphQLType(name string) bool {
|
||||
return name == "SidebarCategorySorting"
|
||||
}
|
||||
|
||||
func (t SidebarCategorySorting) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(string(t))
|
||||
}
|
||||
|
||||
func (t *SidebarCategorySorting) UnmarshalGraphQL(input interface{}) error {
|
||||
chType, ok := input.(string)
|
||||
if !ok {
|
||||
return errors.New("wrong type")
|
||||
}
|
||||
|
||||
*t = SidebarCategorySorting(chType)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ const (
|
||||
|
||||
APIURLSuffixV1 = "/api/v1"
|
||||
APIURLSuffixV4 = "/api/v4"
|
||||
APIURLSuffixV5 = "/api/v5"
|
||||
APIURLSuffix = APIURLSuffixV4
|
||||
)
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/graph-gophers/graphql-go"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -61,6 +63,12 @@ func (cs *CustomStatus) AreDurationAndExpirationTimeValid() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ExpiresAt_ returns the time in a type that has the marshal/unmarshal methods
|
||||
// attached to it.
|
||||
func (cs *CustomStatus) ExpiresAt_() graphql.Time {
|
||||
return graphql.Time{Time: cs.ExpiresAt}
|
||||
}
|
||||
|
||||
func RuneToHexadecimalString(r rune) string {
|
||||
return fmt.Sprintf("%04x", r)
|
||||
}
|
||||
|
||||
@@ -69,6 +69,9 @@ type FeatureFlags struct {
|
||||
|
||||
// Enable Workspace optimization dashboard
|
||||
WorkspaceOptimizationDashboard bool
|
||||
|
||||
// Enable GraphQL feature
|
||||
GraphQL bool
|
||||
}
|
||||
|
||||
func (f *FeatureFlags) SetDefaults() {
|
||||
@@ -93,6 +96,7 @@ func (f *FeatureFlags) SetDefaults() {
|
||||
f.BoardsDataRetention = false
|
||||
f.NormalizeLdapDNs = false
|
||||
f.WorkspaceOptimizationDashboard = false
|
||||
f.GraphQL = false
|
||||
}
|
||||
|
||||
func (f *FeatureFlags) Plugins() map[string]string {
|
||||
|
||||
@@ -34,6 +34,19 @@ func (s *Status) ToJSON() ([]byte, error) {
|
||||
return json.Marshal(sCopy)
|
||||
}
|
||||
|
||||
// The following are some GraphQL methods necessary to return the
|
||||
// data in float64 type. The spec doesn't support 64 bit integers,
|
||||
// so we have to pass the data in float64. The _ at the end is
|
||||
// a hack to keep the attribute name same in GraphQL schema.
|
||||
|
||||
func (s *Status) LastActivityAt_() float64 {
|
||||
return float64(s.LastActivityAt)
|
||||
}
|
||||
|
||||
func (s *Status) DNDEndTime_() float64 {
|
||||
return float64(s.DNDEndTime)
|
||||
}
|
||||
|
||||
func StatusListToJSON(u []*Status) ([]byte, error) {
|
||||
list := make([]Status, len(u))
|
||||
for i, s := range u {
|
||||
|
||||
@@ -125,3 +125,9 @@ func (o *TeamMember) PreUpdate() {
|
||||
func (o *TeamMember) GetRoles() []string {
|
||||
return strings.Fields(o.Roles)
|
||||
}
|
||||
|
||||
// DeleteAt_ returns the deleteAt value in float64. This is necessary to work
|
||||
// with GraphQL since it doesn't support 64 bit integers.
|
||||
func (o *TeamMember) DeleteAt_() float64 {
|
||||
return float64(o.DeleteAt)
|
||||
}
|
||||
|
||||
@@ -409,6 +409,23 @@ func (u *User) PreSave() {
|
||||
}
|
||||
}
|
||||
|
||||
// The following are some GraphQL methods necessary to return the
|
||||
// data in float64 type. The spec doesn't support 64 bit integers,
|
||||
// so we have to pass the data in float64. The _ at the end is
|
||||
// a hack to keep the attribute name same in GraphQL schema.
|
||||
|
||||
func (u *User) CreateAt_() float64 {
|
||||
return float64(u.CreateAt)
|
||||
}
|
||||
|
||||
func (u *User) DeleteAt_() float64 {
|
||||
return float64(u.DeleteAt)
|
||||
}
|
||||
|
||||
func (u *User) LastPictureUpdateAt() float64 {
|
||||
return float64(u.LastPictureUpdate)
|
||||
}
|
||||
|
||||
// PreUpdate should be run before updating the user in the db.
|
||||
func (u *User) PreUpdate() {
|
||||
u.Username = SanitizeUnicode(u.Username)
|
||||
@@ -630,6 +647,15 @@ func (u *User) GetCustomStatus() *CustomStatus {
|
||||
return o
|
||||
}
|
||||
|
||||
func (u *User) CustomStatus() *CustomStatus {
|
||||
var o *CustomStatus
|
||||
|
||||
data := u.Props[UserPropsKeyCustomStatus]
|
||||
_ = json.Unmarshal([]byte(data), &o)
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
func (u *User) ClearCustomStatus() {
|
||||
u.MakeNonNil()
|
||||
u.Props[UserPropsKeyCustomStatus] = ""
|
||||
|
||||
@@ -132,6 +132,24 @@ func (m StringMap) Value() (driver.Value, error) {
|
||||
return string(j), err
|
||||
}
|
||||
|
||||
func (StringMap) ImplementsGraphQLType(name string) bool {
|
||||
return name == "StringMap"
|
||||
}
|
||||
|
||||
func (m StringMap) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal((map[string]string)(m))
|
||||
}
|
||||
|
||||
func (m *StringMap) UnmarshalGraphQL(input interface{}) error {
|
||||
json, ok := input.(map[string]string)
|
||||
if !ok {
|
||||
return errors.New("wrong type")
|
||||
}
|
||||
|
||||
*m = json
|
||||
return nil
|
||||
}
|
||||
|
||||
func (si *StringInterface) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
return nil
|
||||
|
||||
Ссылка в новой задаче
Block a user