├── .gitattributes
├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── authcontext
└── contextvalues.go
├── authentication
├── authentication.go
├── jwt
│ └── jwt.go
├── middleware.go
└── remote.go
├── common
├── ability.go
├── role.go
└── user.go
├── config
├── config.go
├── env.go
├── env_test.go
└── file.go
├── config_example.yaml
├── consts
├── config.go
├── consts.go
└── federation.go
├── db
├── dbx.go
├── dialect
│ ├── dialect.go
│ └── mysql
│ │ ├── base.go
│ │ ├── delete.go
│ │ ├── insert.go
│ │ ├── migration.go
│ │ ├── query.go
│ │ ├── query_test.go
│ │ └── update.go
└── nulluint64.go
├── go.mod
├── go.sum
├── handler
├── README.md
├── graphiql.go
├── handler.go
├── handlerfunc.go
└── playground.go
├── main.go
├── model
├── data
│ ├── instance.go
│ ├── povit.go
│ └── relation.go
├── diff.go
├── domain
│ ├── attribute.go
│ ├── class.go
│ ├── domain.go
│ ├── enum.go
│ ├── method.go
│ └── relation.go
├── graph
│ ├── args.go
│ ├── args_test.go
│ ├── association.go
│ ├── attribute.go
│ ├── class.go
│ ├── entity.go
│ ├── enum.go
│ ├── external.go
│ ├── graph.go
│ ├── interface.go
│ ├── method.go
│ ├── partial.go
│ ├── propertier.go
│ ├── relation.go
│ └── table.go
├── meta
│ ├── attributemeta.go
│ ├── classmeta.go
│ ├── content.go
│ ├── meta.go
│ ├── methodmeta.go
│ ├── predefined.go
│ ├── relationmeta.go
│ └── type.go
├── model.go
└── table
│ ├── column.go
│ └── table.go
├── repository
├── authorization.go
├── connection.go
├── connectiondo.go
├── convert.go
├── db.go
├── metas.go
├── migrate.go
└── repository.go
├── resolve
├── convertid.go
├── file.go
├── install.go
├── loaders.go
├── logout.go
├── me.go
├── middleware.go
├── mutation.go
├── publish.go
├── query.go
└── verifier.go
├── scalars
├── federation.go
├── json.go
└── upload.go
├── schema
├── aggregate.go
├── cache.go
├── comparison.go
├── enum.go
├── federation.go
├── federationmutation.go
├── federationquery.go
├── globals.go
├── input.go
├── interface.go
├── model.go
├── mutation.go
├── object.go
├── orderby.go
├── property.go
├── query.go
├── queryargs.go
├── relation.go
├── resolve.go
├── schema.go
├── service.go
├── subscription.go
└── upload.go
├── sdlview.html
├── storage
└── file.go
├── utils
├── array.go
├── debug.go
├── id.go
├── json.go
├── map.go
├── object.go
└── string.go
└── 备忘.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 |
8 | # Test binary, build with `go test -c`
9 | *.test
10 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
13 |
14 | config.yaml
15 | uploads
16 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.18-buster as builder
2 |
3 | ENV GOPROXY=https://goproxy.cn,direct
4 | ENV GO111MODULE=on
5 | ENV GOFLAGS=-mod=vendor
6 | ENV APP_HOME /go/src/entify
7 |
8 | WORKDIR "$APP_HOME"
9 | ADD . "$APP_HOME"
10 |
11 | RUN go mod download
12 | RUN go mod verify
13 | RUN go get ./...
14 | RUN go mod vendor
15 | RUN go build -o entify
16 |
17 | EXPOSE 4000
18 | CMD ["./entify"]
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Water.Li
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # entify
2 | GraphQL low code engine, suport micro-service
3 |
--------------------------------------------------------------------------------
/authcontext/contextvalues.go:
--------------------------------------------------------------------------------
1 | package authcontext
2 |
3 | import (
4 | "github.com/graphql-go/graphql"
5 | "rxdrag.com/entify/common"
6 | "rxdrag.com/entify/consts"
7 | )
8 |
9 | type ContextValues struct {
10 | Token string
11 | Me *common.User
12 | QueryUserCache map[string][]string
13 | }
14 |
15 | func ParseContextValues(p graphql.ResolveParams) ContextValues {
16 | values := p.Context.Value(consts.CONTEXT_VALUES)
17 | if values == nil {
18 | panic("Not set CONTEXT_VALUES in context")
19 | }
20 |
21 | return values.(ContextValues)
22 | }
23 |
--------------------------------------------------------------------------------
/authentication/authentication.go:
--------------------------------------------------------------------------------
1 | package authentication
2 |
3 | import (
4 | "database/sql"
5 | "errors"
6 | "fmt"
7 |
8 | "golang.org/x/crypto/bcrypt"
9 | "rxdrag.com/entify/authentication/jwt"
10 | "rxdrag.com/entify/common"
11 | "rxdrag.com/entify/config"
12 | "rxdrag.com/entify/db/dialect"
13 | "rxdrag.com/entify/repository"
14 | )
15 |
16 | var TokenCache = map[string]*common.User{}
17 |
18 | func loadUser(loginName string) *common.User {
19 | con, err := repository.Open(repository.NewSupperVerifier())
20 | if err != nil {
21 | fmt.Println(err)
22 | panic(err)
23 | }
24 | var user common.User
25 | var isSupper sql.NullBool
26 | var isDemo sql.NullBool
27 |
28 | sqlBuilder := dialect.GetSQLBuilder()
29 | err = con.Dbx.QueryRow(sqlBuilder.BuildMeSQL(), loginName).Scan(
30 | &user.Id,
31 | &user.Name,
32 | &user.LoginName,
33 | &isSupper,
34 | &isDemo,
35 | )
36 | switch {
37 | case err == sql.ErrNoRows:
38 | return nil
39 | case err != nil:
40 | panic(err.Error())
41 | }
42 |
43 | user.IsSupper = isSupper.Bool
44 | user.IsDemo = isDemo.Bool
45 |
46 | rows, err := con.Dbx.Query(sqlBuilder.BuildRolesSQL(), user.Id)
47 | if err != nil {
48 | panic(err.Error())
49 | }
50 | for rows.Next() {
51 | var role common.Role
52 | err = rows.Scan(&role.Id, &role.Name)
53 | if err != nil {
54 | panic(err.Error())
55 | }
56 | user.Roles = append(user.Roles, role)
57 | }
58 | return &user
59 | }
60 |
61 | func Login(loginName, pwd string) (string, error) {
62 | con, err := repository.Open(repository.NewSupperVerifier())
63 | if err != nil {
64 | fmt.Println(err)
65 | panic(err)
66 | }
67 | sqlBuilder := dialect.GetSQLBuilder()
68 | var password string
69 | err = con.Dbx.QueryRow(sqlBuilder.BuildLoginSQL(), loginName).Scan(&password)
70 | if err != nil {
71 | fmt.Println(err)
72 | return "", errors.New("Login failed!")
73 | }
74 |
75 | err = bcrypt.CompareHashAndPassword([]byte(password), []byte(pwd)) //验证(对比)
76 | if err != nil {
77 | fmt.Println(err, pwd, password)
78 | return "", errors.New("Password error!")
79 | }
80 |
81 | token, err := jwt.GenerateToken(loginName)
82 | if err != nil {
83 | panic(err.Error())
84 | }
85 |
86 | user := loadUser(loginName)
87 | TokenCache[token] = user
88 | return token, err
89 | }
90 |
91 | func Logout(token string) {
92 | TokenCache[token] = nil
93 | }
94 |
95 | func GetUserByToken(token string) (*common.User, error) {
96 | authUrl := config.AuthUrl()
97 | if authUrl == "" {
98 | return TokenCache[token], nil
99 | } else {
100 | return meFromRemote(token)
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/authentication/jwt/jwt.go:
--------------------------------------------------------------------------------
1 | package jwt
2 |
3 | import (
4 | "log"
5 | "time"
6 |
7 | "github.com/dgrijalva/jwt-go"
8 | )
9 |
10 | // secret key being used to sign tokens
11 | var (
12 | SecretKey = []byte("secret")
13 | )
14 |
15 | // GenerateToken generates a jwt token and assign a username to it's claims and return it
16 | func GenerateToken(username string) (string, error) {
17 | token := jwt.New(jwt.SigningMethodHS256)
18 | /* Create a map to store our claims */
19 | claims := token.Claims.(jwt.MapClaims)
20 | /* Set token claims */
21 | claims["username"] = username
22 | claims["exp"] = time.Now().Add(time.Hour * 24).Unix()
23 | tokenString, err := token.SignedString(SecretKey)
24 | if err != nil {
25 | log.Fatal("Error in Generating key")
26 | return "", err
27 | }
28 | return tokenString, nil
29 | }
30 |
31 | // ParseToken parses a jwt token and returns the username in it's claims
32 | func ParseToken(tokenStr string) (string, error) {
33 | token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
34 | return SecretKey, nil
35 | })
36 | if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
37 | username := claims["username"].(string)
38 | return username, nil
39 | } else {
40 | return "", err
41 | }
42 | }
43 |
44 | // 原文链接:https://www.howtographql.com/graphql-go/6-authentication/
45 |
--------------------------------------------------------------------------------
/authentication/middleware.go:
--------------------------------------------------------------------------------
1 | package authentication
2 |
3 | import (
4 | "context"
5 | "net/http"
6 | "strings"
7 | "time"
8 |
9 | "rxdrag.com/entify/authcontext"
10 | "rxdrag.com/entify/consts"
11 | )
12 |
13 | // AuthMiddleware 传递公共参数中间件
14 | func AuthMiddleware(next http.Handler) http.Handler {
15 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
16 | //为了测试loading状态,生产版需要删掉
17 | time.Sleep(time.Duration(300) * time.Millisecond)
18 |
19 | reqToken := r.Header.Get(consts.AUTHORIZATION)
20 | splitToken := strings.Split(reqToken, consts.BEARER)
21 | v := authcontext.ContextValues{}
22 | if len(splitToken) == 2 {
23 | reqToken = splitToken[1]
24 | if reqToken != "" {
25 | v.Token = reqToken
26 | me, err := GetUserByToken(reqToken)
27 | if err != nil {
28 | http.Error(w, err.Error(), http.StatusInternalServerError)
29 | return
30 | }
31 | v.Me = me
32 | }
33 | }
34 | ctx := context.WithValue(r.Context(), consts.CONTEXT_VALUES, v)
35 | ctx = context.WithValue(ctx, consts.HOST, r.Host)
36 | next.ServeHTTP(w, r.WithContext(ctx))
37 | })
38 | }
39 |
40 | func CorsMiddleware(next http.Handler) http.Handler {
41 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
42 | //设置跨域
43 | w.Header().Set("Access-Control-Allow-Origin", "*")
44 | w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
45 | w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization")
46 | //w.Header().Set("Access-Control-Allow-Credentials", "true")
47 | next.ServeHTTP(w, r)
48 | })
49 | }
50 |
--------------------------------------------------------------------------------
/authentication/remote.go:
--------------------------------------------------------------------------------
1 | package authentication
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | "errors"
7 | "fmt"
8 | "io/ioutil"
9 | "net/http"
10 | "time"
11 |
12 | "github.com/mitchellh/mapstructure"
13 | "rxdrag.com/entify/common"
14 | "rxdrag.com/entify/config"
15 | "rxdrag.com/entify/consts"
16 | )
17 |
18 | func meFromRemote(token string) (*common.User, error) {
19 | authUrl := config.AuthUrl()
20 | jsonData := map[string]string{
21 | "query": `
22 | {
23 | me {
24 | id
25 | name
26 | loginName
27 | isSupper
28 | isDemo
29 | roles {
30 | id
31 | name
32 | }
33 | }
34 | }
35 | `,
36 | }
37 | jsonValue, _ := json.Marshal(jsonData)
38 | request, err := http.NewRequest("POST", authUrl, bytes.NewBuffer(jsonValue))
39 | request.Header.Set(consts.AUTHORIZATION, consts.BEARER+token)
40 | client := &http.Client{Timeout: time.Second * 10}
41 | response, err := client.Do(request)
42 | if err != nil {
43 | fmt.Printf("The HTTP request failed with error %s\n", err)
44 | return nil, errors.New("Can't access authentication service: " + authUrl)
45 | }
46 | defer response.Body.Close()
47 |
48 | data, _ := ioutil.ReadAll(response.Body)
49 | var user common.User
50 | var userJson map[string]interface{}
51 | json.Unmarshal(data, &userJson)
52 | fmt.Println(userJson)
53 | if userJson["data"] != nil {
54 | meJson := userJson["data"].(map[string]interface{})["me"]
55 | if meJson != nil {
56 | err = mapstructure.Decode(meJson, &user)
57 | if err != nil {
58 | panic(err.Error())
59 | }
60 | }
61 | return &user, nil
62 | }
63 | return nil, nil
64 | }
65 |
--------------------------------------------------------------------------------
/common/ability.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | type Ability struct {
4 | Id uint64 `json:"id"`
5 | EntityUuid string `json:"entityUuid"`
6 | ColumnUuid string `json:"columnUuid"`
7 | Can bool `json:"can"`
8 | Expression string `json:"expression"`
9 | AbilityType string `json:"abilityType"`
10 | RoleId uint64 `json:"roleId"`
11 | }
12 |
--------------------------------------------------------------------------------
/common/role.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import "strconv"
4 |
5 | type Role struct {
6 | Id string `json:"id"`
7 | Name string `json:"name"`
8 | }
9 |
10 | func (r Role) Uint64Id() uint64 {
11 | number, err := strconv.ParseUint(r.Id, 10, 64)
12 | if err != nil {
13 | panic(err.Error())
14 | }
15 | return number
16 | }
17 |
--------------------------------------------------------------------------------
/common/user.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import "strconv"
4 |
5 | type User struct {
6 | Id string `json:"id"` //ID类型,查询时需要用字符串接收
7 | Name string `json:"name"`
8 | LoginName string `json:"loginName"`
9 | Roles []Role `json:"roles"`
10 | IsSupper bool `json:"isSupper"`
11 | IsDemo bool `json:"isDemo"`
12 | }
13 |
14 | func (u User) Uint64Id() uint64 {
15 | number, err := strconv.ParseUint(u.Id, 10, 64)
16 | if err != nil {
17 | panic(err.Error())
18 | }
19 | return number
20 | }
21 |
--------------------------------------------------------------------------------
/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "fmt"
5 |
6 | "rxdrag.com/entify/consts"
7 | )
8 |
9 | const TABLE_NAME_MAX_LENGTH = 64
10 |
11 | var fileCfg Config
12 | var envCfg Config
13 |
14 | type DbConfig struct {
15 | Driver string `json:"driver"`
16 | User string `json:"user"`
17 | Password string `json:"password"`
18 | Host string `json:"host"`
19 | Port string `json:"port"`
20 | Database string `json:"database"`
21 | }
22 |
23 | type Config interface {
24 | getString(key string) string
25 | getBool(key string) bool
26 | getInt(key string) int
27 | }
28 |
29 | func GetString(key string) string {
30 | str := envCfg.getString(key)
31 | if str == "" {
32 | str = fileCfg.getString(key)
33 | }
34 | return str
35 | }
36 |
37 | func GetBool(key string) bool {
38 | boolValue := envCfg.getBool(key)
39 | if !boolValue {
40 | boolValue = fileCfg.getBool(key)
41 | }
42 | return boolValue
43 | }
44 |
45 | func GetInt(key string) int {
46 | intValue := envCfg.getInt(key)
47 | if intValue == 0 {
48 | intValue = fileCfg.getInt(key)
49 | }
50 | return intValue
51 | }
52 |
53 | func GetDbConfig() DbConfig {
54 | var cfg DbConfig
55 | cfg.Driver = GetString(consts.DB_DRIVER)
56 | cfg.Database = GetString(consts.DB_DATABASE)
57 | cfg.Host = GetString(consts.DB_HOST)
58 | cfg.Port = GetString(consts.DB_PORT)
59 | cfg.User = GetString(consts.DB_USER)
60 | cfg.Password = GetString(consts.DB_PASSWORD)
61 | if cfg.Driver == "" {
62 | cfg.Driver = "mysql"
63 | }
64 | return cfg
65 | }
66 |
67 | func ServiceId() int {
68 | serviceId := GetInt(consts.SERVICE_ID)
69 | if serviceId == 0 {
70 | fmt.Println("Not set service id, use 1 as service id")
71 | return 1
72 | }
73 | return serviceId
74 | }
75 |
76 | func AuthUrl() string {
77 | return GetString(consts.AUTH_URL)
78 | }
79 |
80 | func Storage() string {
81 | return GetString(consts.STORAGE)
82 | }
83 |
84 | func init() {
85 | fileCfg = newFileConfig()
86 | envCfg = newEnvConfig()
87 | }
88 |
--------------------------------------------------------------------------------
/config/env.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "strconv"
5 |
6 | "github.com/spf13/viper"
7 | "rxdrag.com/entify/consts"
8 | )
9 |
10 | type EnvConfig struct {
11 | v *viper.Viper
12 | }
13 |
14 | const (
15 | TRUE = "true"
16 | FALSE = "false"
17 | )
18 |
19 | func newEnvConfig() *EnvConfig {
20 | var e EnvConfig
21 | e.v = viper.New()
22 | e.v.BindEnv(consts.DB_USER)
23 | e.v.BindEnv(consts.DB_DRIVER)
24 | e.v.BindEnv(consts.DB_PASSWORD)
25 | e.v.BindEnv(consts.DB_HOST)
26 | e.v.BindEnv(consts.DB_PORT)
27 | e.v.BindEnv(consts.DB_DATABASE)
28 | e.v.BindEnv(consts.SERVICE_ID)
29 | e.v.BindEnv(consts.AUTH_URL)
30 | e.v.BindEnv(consts.STORAGE)
31 | return &e
32 | }
33 |
34 | func (e *EnvConfig) getString(key string) string {
35 | str := e.v.Get(key)
36 | if str != nil {
37 | return str.(string)
38 | }
39 | return ""
40 | }
41 |
42 | func (e *EnvConfig) getBool(key string) bool {
43 | return e.v.Get(key) == TRUE
44 | }
45 |
46 | func (e *EnvConfig) getInt(key string) int {
47 | value := e.getString(key)
48 | if value == "" {
49 | return 0
50 | }
51 | i, err := strconv.ParseInt(value, 0, 32)
52 | if err != nil {
53 | return int(i)
54 | }
55 | return 0
56 | }
57 |
--------------------------------------------------------------------------------
/config/env_test.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "testing"
5 |
6 | "rxdrag.com/entify/consts"
7 | )
8 |
9 | func TestGetString(t *testing.T) {
10 | if GetString(consts.DB_DRIVER) != "mysql" {
11 | t.Error("Getstring Error:" + GetString(consts.DB_DRIVER))
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/config/file.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "github.com/spf13/viper"
5 | "rxdrag.com/entify/consts"
6 | )
7 |
8 | const (
9 | PATH = "."
10 | CONFIG_TYPE = "yaml"
11 | CONFIG_NAME = "config"
12 | )
13 |
14 | type FileConfig struct {
15 | v *viper.Viper
16 | }
17 |
18 | func newFileConfig() *FileConfig {
19 | var f FileConfig
20 | f.v = viper.New()
21 | f.v.SetConfigName(CONFIG_NAME) // name of config file (without extension)
22 | f.v.SetConfigType(CONFIG_TYPE) // REQUIRED if the config file does not have the extension in the name
23 | f.v.AddConfigPath(PATH)
24 |
25 | err := f.v.ReadInConfig() // Find and read the config file
26 | if err != nil {
27 | panic("Env not set and can not find config file")
28 | }
29 |
30 | f.v.SetDefault(consts.SERVICE_ID, 1)
31 | f.v.SetDefault(consts.DB_DRIVER, "mysql")
32 | return &f
33 | }
34 |
35 | func (f *FileConfig) getString(key string) string {
36 | return f.v.GetString(key)
37 | }
38 |
39 | func (f *FileConfig) getBool(key string) bool {
40 | return f.v.GetBool(key)
41 | }
42 |
43 | func (f *FileConfig) getInt(key string) int {
44 | return f.v.GetInt(key)
45 | }
46 |
--------------------------------------------------------------------------------
/config_example.yaml:
--------------------------------------------------------------------------------
1 | database: database_name
2 | driver: mysql
3 | host: localhost_name
4 | service_id: 1
5 | password: your password
6 | port: "3306"
7 | user: root
8 | auth_url: ""
9 | #storage: "local"
--------------------------------------------------------------------------------
/consts/config.go:
--------------------------------------------------------------------------------
1 | package consts
2 |
3 | const (
4 | CONFIG_PREFIX = "entify"
5 | DB_DRIVER = "driver"
6 | DB_USER = "user"
7 | DB_PASSWORD = "password"
8 | DB_HOST = "host"
9 | DB_PORT = "port"
10 | DB_DATABASE = "database"
11 | SERVICE_ID = "service_id"
12 | AUTH_URL = "auth_url"
13 | STORAGE = "storage"
14 | UPLOAD_PATH = "./static/uploads"
15 | UPLOAD_PRIFIX = "/uploads"
16 | )
17 |
18 | const (
19 | LOCAL = "local"
20 | ALIYUN = "aliyun"
21 | )
22 |
23 | const (
24 | URL = "url"
25 | ADMIN = "admin"
26 | ADMINPASSWORD = "password"
27 | WITHDEMO = "withDemo"
28 | )
29 |
30 | const (
31 | SERVICE_NODE_TYPE = "serviceType"
32 | NORMAL_SERVICE = "normal"
33 | AUTH_SERVICE = "auth"
34 | )
35 |
--------------------------------------------------------------------------------
/consts/consts.go:
--------------------------------------------------------------------------------
1 | package consts
2 |
3 | const LOADERS = "loaders"
4 |
5 | const (
6 | ROOT_QUERY_NAME = "Query"
7 | ROOT_MUTATION_NAME = "Mutation"
8 | ROOT_SUBSCRIPTION_NAME = "Subscription"
9 | LOGIN = "login"
10 | LOGIN_NAME = "loginName"
11 | PASSWORD = "password"
12 | LOGOUT = "logout"
13 | ME = "me"
14 | IS_SUPPER = "isSupper"
15 | IS_DEMO = "isDemo"
16 | PUBLISH = "publish"
17 | ROLLBACK = "rollback"
18 | SYNC_META = "syncMeta"
19 | NAME = "name"
20 | INSTALLED = "installed"
21 | CAN_UPLOAD = "canUpload"
22 |
23 | ONE = "one"
24 | QUERY = "query"
25 | AGGREGATE = "aggregate"
26 | FIELDS = "Fields"
27 | NODES = "nodes"
28 | INPUT = "Input"
29 | SET_INPUT = "Set"
30 | UPSERT = "upsert"
31 | UPSERT_ONE = "upsertOne"
32 | INSERT = "insert"
33 | INSERT_ONE = "insertOne"
34 | UPDATE = "update"
35 | UPDATE_ONE = "updateOne"
36 | DELETE = "delete"
37 | BY_ID = "ById"
38 | SET = "set"
39 | HAS_MANY = "HasMany"
40 | HAS_ONE = "HasOne"
41 | ENTITY = "Entity"
42 |
43 | ARG_DISTINCTON string = "distinctOn"
44 | ARG_LIMIT string = "limit"
45 | ARG_OFFSET string = "offset"
46 | ARG_ORDERBY string = "orderBy"
47 | ARG_WHERE string = "where"
48 |
49 | ARG_ADD string = "add"
50 | ARG_DELETE string = "delete"
51 | ARG_UPDATE string = "update"
52 | ARG_SYNC string = "sync"
53 | ARG_CASCADE string = "cascade"
54 |
55 | ARG_AND string = "_and"
56 | ARG_NOT string = "_not"
57 | ARG_OR string = "_or"
58 | )
59 |
60 | //EQ("="), GTE(">="), GT(">"), LT("<"), LTE("<=");
61 | const (
62 | ARG_EQ string = "_eq"
63 | ARG_GT string = "_gt"
64 | ARG_GTE string = "_gte"
65 | ARG_IN string = "_in"
66 | ARG_ISNULL string = "_isNull"
67 | ARG_LT string = "_lt"
68 | ARG_LTE string = "_lte"
69 | ARG_NOTEQ string = "_notEq"
70 | ARG_NOTIN string = "_notIn"
71 |
72 | ARG_ILIKE string = "_iLike"
73 | // ARG_IREGEX string = "_iregex"
74 | ARG_LIKE string = "_like"
75 | ARG_NOTILIKE string = "_notILike"
76 | // ARG_NOTIREGEX string = "_notIRegex"
77 | ARG_NOTLIKE string = "_notLike"
78 | ARG_NOTREGEX string = "_notRegexp"
79 | // ARG_NOTSIMILAR string = "_notSimilar"
80 | ARG_REGEX string = "_regexp"
81 | // ARG_SIMILAR string = "_similar"
82 | )
83 |
84 | const (
85 | ARG_COUNT string = "count"
86 | ARG_COLUMNS string = "columns"
87 | ARG_DISTINCT string = "distinct"
88 | )
89 |
90 | const (
91 | ARG_OBJECT string = "object"
92 | ARG_OBJECTS string = "objects"
93 | RESPONSE_RETURNING string = "returning"
94 | RESPONSE_AFFECTEDROWS string = "affectedRows"
95 | ARG_SET string = "set"
96 | ARG_FILE string = "file"
97 | )
98 |
99 | const (
100 | UUID string = "uuid"
101 | INNERID string = "innerId"
102 | TYPE string = "type"
103 | )
104 |
105 | /**
106 | * Meta实体用到的常量
107 | **/
108 | const (
109 | META_ENTITY_NAME string = "Meta"
110 | META_ID string = "id"
111 | META_STATUS string = "status"
112 | META_CONTENT string = "content"
113 | META_PUBLISHEDAT string = "publishedAt"
114 | META_CREATEDAT string = "createdAt"
115 | META_UPDATEDAT string = "updatedAt"
116 |
117 | META_CLASSES string = "classes"
118 | META_RELATIONS string = "relations"
119 | )
120 |
121 | const (
122 | MEDIA_ENTITY_NAME = "Media"
123 | MEDIA_UUID = "MEDIA_ENTITY_UUID"
124 | )
125 |
126 | const (
127 | ID_SUFFIX string = "_id"
128 | PIVOT string = "pivot"
129 | INDEX_SUFFIX string = "_idx"
130 | SUFFIX_SOURCE string = "_source"
131 | SUFFIX_TARGET string = "_target"
132 | )
133 |
134 | const (
135 | ID string = "id"
136 | OF string = "Of"
137 | )
138 |
139 | const (
140 | DELETED_AT string = "deletedAt"
141 | )
142 |
143 | const (
144 | BOOLEXP string = "BoolExp"
145 | ORDERBY string = "OrderBy"
146 | DISTINCTEXP string = "DistinctExp"
147 | MUTATION_RESPONSE string = "MutationResponse"
148 | )
149 |
150 | const ASSOCIATION_OWNER_ID = "owner__rx__id"
151 |
152 | const META_USER = "User"
153 | const META_ROLE = "Role"
154 |
155 | const SYSTEM = "System"
156 | const CREATEDATE = "createDate"
157 | const UPDATEDATE = "updateDate"
158 |
159 | const (
160 | TOKEN = "token"
161 | AUTHORIZATION = "Authorization"
162 | BEARER = "Bearer "
163 | CONTEXT_VALUES = "values"
164 | HOST = "host"
165 | )
166 |
167 | const (
168 | ABILITY_UUID = "META_ABILITY_UUID"
169 | USER_UUID = "META_USER_UUID"
170 | ROLE_UUID = "META_ROLE_UUID"
171 | )
172 | const (
173 | META_INNER_ID = 1
174 | ENTITY_AUTH_SETTINGS_INNER_ID = 2
175 | Ability_INNER_ID = 3
176 | USER_INNER_ID = 4
177 | ROLE_INNER_ID = 5
178 | MEDIA_INNER_ID = 6
179 | ROLE_USER_RELATION_INNER_ID = 101
180 | )
181 |
182 | const ROOT = "root"
183 |
184 | //普通角色的ID永远不会是1
185 | const GUEST_ROLE_ID = "1"
186 | const PREDEFINED_QUERYUSER = "$queryUser"
187 | const PREDEFINED_ME = "$me"
188 |
189 | const NO_PERMISSION = "No permission to access data"
190 |
191 | const (
192 | FILE = "File"
193 | FILE_NAME = "fileName"
194 | FILE_SIZE = "size"
195 | FILE_MIMETYPE = "mimeType"
196 | FILE_URL = "url"
197 | File_EXTNAME = "extName"
198 | FILE_THMUBNAIL = "thumbnail"
199 | FILE_RESIZE = "resize"
200 | FILE_WIDTH = "width"
201 | FILE_HEIGHT = "height"
202 | )
203 |
--------------------------------------------------------------------------------
/consts/federation.go:
--------------------------------------------------------------------------------
1 | package consts
2 |
3 | const (
4 | SCALAR_ANY = "_Any"
5 | SCALAR_FIELDSET = "_FieldSet"
6 | SERVICE_TYPE = "_Service"
7 | ENTITY_TYPE = "_Entity"
8 | SERVICE = "_service"
9 | ENTITIES = "_entities"
10 | SDL = "sdl"
11 | REPRESENTATIONS = "representations"
12 | TYPENAME = "__typename"
13 | )
14 |
15 | const (
16 | EXTERNAL = "external"
17 | REQUIRES = "requires"
18 | PROVIDES = "provides"
19 | KEY = "key"
20 | EXTENDS = "extends"
21 | ARG_FIELDS = "fields"
22 | )
23 |
--------------------------------------------------------------------------------
/db/dbx.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "database/sql"
5 | "log"
6 | )
7 |
8 | var openedDB *sql.DB
9 |
10 | type Dbx struct {
11 | db *sql.DB
12 | tx *sql.Tx
13 | }
14 |
15 | func (d *Dbx) BeginTx() error {
16 | tx, err := d.db.Begin()
17 | if err != nil {
18 | return err
19 | }
20 |
21 | d.tx = tx
22 | return nil
23 | }
24 |
25 | func (d *Dbx) ClearTx() {
26 | d.validateTx()
27 | err := d.Rollback()
28 | if err != sql.ErrTxDone && err != nil {
29 | log.Fatalln(err)
30 | }
31 | }
32 |
33 | func (d *Dbx) validateDb() {
34 | if d.db == nil {
35 | panic("Not init connection with db")
36 | }
37 | }
38 |
39 | func (d *Dbx) validateTx() {
40 | if d.tx == nil {
41 | panic("Not init connection with tx")
42 | }
43 | }
44 |
45 | func (d *Dbx) Exec(sql string, args ...interface{}) (sql.Result, error) {
46 | d.validateDb()
47 | if d.tx != nil {
48 | return d.tx.Exec(sql, args...)
49 | }
50 | return d.db.Exec(sql, args...)
51 | }
52 |
53 | func (d *Dbx) Query(query string, args ...interface{}) (*sql.Rows, error) {
54 | d.validateDb()
55 | if d.tx != nil {
56 | return d.tx.Query(query, args...)
57 | } else {
58 | return d.db.Query(query, args...)
59 | }
60 | }
61 |
62 | func (d *Dbx) QueryRow(query string, args ...interface{}) *sql.Row {
63 | d.validateDb()
64 | if d.tx != nil {
65 | return d.tx.QueryRow(query, args...)
66 | } else {
67 | return d.db.QueryRow(query, args...)
68 | }
69 | }
70 |
71 | // func (c *Dbx) Close() error {
72 | // c.validateDb()
73 | // return c.db.Close()
74 | // }
75 |
76 | func (d *Dbx) Commit() error {
77 | d.validateTx()
78 | return d.tx.Commit()
79 | }
80 | func (c *Dbx) Rollback() error {
81 | c.validateTx()
82 | return c.tx.Rollback()
83 | }
84 |
85 | func Open(driver string, config string) (*Dbx, error) {
86 | if openedDB == nil {
87 | db, err := sql.Open(driver, config)
88 | openedDB = db
89 | if err != nil {
90 | return nil, err
91 | }
92 | }
93 | con := Dbx{
94 | db: openedDB,
95 | }
96 | return &con, nil
97 | }
98 |
99 | func Close() error {
100 | if openedDB != nil {
101 | err := openedDB.Close()
102 | openedDB = nil
103 | return err
104 | }
105 |
106 | return nil
107 | }
108 |
--------------------------------------------------------------------------------
/db/dialect/dialect.go:
--------------------------------------------------------------------------------
1 | package dialect
2 |
3 | import (
4 | "rxdrag.com/entify/db/dialect/mysql"
5 | "rxdrag.com/entify/model"
6 | "rxdrag.com/entify/model/data"
7 | "rxdrag.com/entify/model/graph"
8 | "rxdrag.com/entify/model/table"
9 | )
10 |
11 | const (
12 | MySQL = "mysql"
13 | )
14 |
15 | type SQLBuilder interface {
16 | BuildMeSQL() string
17 | BuildRolesSQL() string
18 | BuildLoginSQL() string
19 | BuildCreateMetaSQL() string
20 | BuildCreateAbilitySQL() string
21 | BuildCreateEntityAuthSettingsSQL() string
22 | BuildBoolExp(argEntity *graph.ArgEntity, where map[string]interface{}) (string, []interface{})
23 | BuildFieldExp(fieldName string, fieldArgs map[string]interface{}) (string, []interface{})
24 |
25 | BuildCreateTableSQL(table *table.Table) string
26 | BuildDeleteTableSQL(table *table.Table) string
27 | BuildColumnSQL(column *table.Column) string
28 | BuildModifyTableAtoms(diff *model.TableDiff) []model.ModifyAtom
29 | ColumnTypeSQL(column *table.Column) string
30 |
31 | BuildQuerySQLBody(argEntity *graph.ArgEntity, fields []*graph.Attribute) string
32 | BuildWhereSQL(argEntity *graph.ArgEntity, fields []*graph.Attribute, where map[string]interface{}) (string, []interface{})
33 | BuildOrderBySQL(argEntity *graph.ArgEntity, orderBy interface{}) string
34 | //BuildQuerySQL(tableName string, fields []*graph.Attribute, args map[string]interface{}) (string, []interface{})
35 |
36 | BuildInsertSQL(fields []*data.Field, table *table.Table) string
37 | BuildUpdateSQL(id uint64, fields []*data.Field, table *table.Table) string
38 |
39 | BuildQueryByIdsSQL(entity *graph.Entity, idCounts int) string
40 | BuildClearAssociationSQL(ownerId uint64, tableName string, ownerFieldName string) string
41 | BuildQueryAssociatedInstancesSQL(entity *graph.Entity,
42 | ownerId uint64,
43 | povitTableName string,
44 | ownerFieldName string,
45 | typeFieldName string,
46 | ) string
47 | BuildBatchAssociationBodySQL(
48 | argEntity *graph.ArgEntity,
49 | fields []*graph.Attribute,
50 | povitTableName string,
51 | ownerFieldName string,
52 | typeFieldName string,
53 | ids []uint64,
54 | ) string
55 | BuildDeleteSQL(id uint64, tableName string) string
56 | BuildSoftDeleteSQL(id uint64, tableName string) string
57 |
58 | BuildQueryPovitSQL(povit *data.AssociationPovit) string
59 | BuildInsertPovitSQL(povit *data.AssociationPovit) string
60 | BuildDeletePovitSQL(povit *data.AssociationPovit) string
61 |
62 | BuildTableCheckSQL(name string, database string) string
63 | }
64 |
65 | func GetSQLBuilder() SQLBuilder {
66 | var builder mysql.MySQLBuilder
67 | return &builder
68 | }
69 |
--------------------------------------------------------------------------------
/db/dialect/mysql/base.go:
--------------------------------------------------------------------------------
1 | package mysql
2 |
3 | import (
4 | "fmt"
5 |
6 | "rxdrag.com/entify/consts"
7 | )
8 |
9 | func (*MySQLBuilder) BuildMeSQL() string {
10 | return "select id, name, loginName, isSupper, isDemo from user where loginName = ?"
11 | }
12 |
13 | func (*MySQLBuilder) BuildRolesSQL() string {
14 | povit := fmt.Sprintf(
15 | "%s_%d_%d_%d",
16 | consts.PIVOT,
17 | consts.ROLE_INNER_ID,
18 | consts.ROLE_USER_RELATION_INNER_ID,
19 | consts.USER_INNER_ID,
20 | )
21 | return fmt.Sprintf("select a.id, a.name from role a left join %s b on a.id = b.role where b.user = ?", povit)
22 | }
23 |
24 | func (*MySQLBuilder) BuildLoginSQL() string {
25 | return "select password from user where loginName = ?"
26 | }
27 |
28 | func (*MySQLBuilder) BuildCreateMetaSQL() string {
29 | return `CREATE TABLE meta (
30 | id bigint NOT NULL AUTO_INCREMENT,
31 | content json DEFAULT NULL,
32 | publishedAt datetime DEFAULT NULL,
33 | createdAt datetime DEFAULT NULL,
34 | updatedAt datetime DEFAULT NULL,
35 | status varchar(45) DEFAULT NULL,
36 | PRIMARY KEY (id)
37 | ) ENGINE=InnoDB AUTO_INCREMENT=1507236403010867251 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
38 | `
39 | }
40 | func (*MySQLBuilder) BuildCreateAbilitySQL() string {
41 | return `CREATE TABLE ability (
42 | id bigint NOT NULL AUTO_INCREMENT,
43 | entityUuid varchar(255) NOT NULL,
44 | columnUuid varchar(255) DEFAULT NULL,
45 | can tinyint(1) NOT NULL,
46 | expression text NOT NULL,
47 | abilityType varchar(128) NOT NULL,
48 | roleId bigint NOT NULL,
49 | PRIMARY KEY (id)
50 | ) ENGINE=InnoDB AUTO_INCREMENT=4503621102206976 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
51 | `
52 | }
53 | func (*MySQLBuilder) BuildCreateEntityAuthSettingsSQL() string {
54 | return `CREATE TABLE entity_auth_settings (
55 | id bigint NOT NULL AUTO_INCREMENT,
56 | entityUuid varchar(255) NOT NULL,
57 | expand tinyint(1) NOT NULL,
58 | PRIMARY KEY (id),
59 | UNIQUE KEY entityUuid_UNIQUE (entityUuid)
60 | ) ENGINE=InnoDB AUTO_INCREMENT=4503616807239680 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
61 | `
62 | }
63 |
64 | func (b *MySQLBuilder) BuildTableCheckSQL(name string, database string) string {
65 | return fmt.Sprintf(
66 | "SELECT COUNT(*) FROM information_schema.TABLES WHERE table_name ='%s' AND table_schema ='%s'",
67 | name,
68 | database,
69 | )
70 | }
71 |
72 | func nullableString(nullable bool) string {
73 | if nullable {
74 | return " NULL "
75 | }
76 | return " NOT NULL "
77 | }
78 |
--------------------------------------------------------------------------------
/db/dialect/mysql/delete.go:
--------------------------------------------------------------------------------
1 | package mysql
2 |
3 | import (
4 | "fmt"
5 | "time"
6 |
7 | "rxdrag.com/entify/consts"
8 | "rxdrag.com/entify/model/data"
9 | )
10 |
11 | func (b *MySQLBuilder) BuildDeleteSQL(id uint64, tableName string) string {
12 | sql := fmt.Sprintf(
13 | "DELETE FROM `%s` WHERE (`%s` = '%d')",
14 | tableName,
15 | "id",
16 | id,
17 | )
18 | return sql
19 | }
20 |
21 | func (b *MySQLBuilder) BuildSoftDeleteSQL(id uint64, tableName string) string {
22 | sql := fmt.Sprintf(
23 | "UPDATE `%s` SET `%s` = '%s' WHERE (`%s` = %d)",
24 | tableName,
25 | consts.DELETED_AT,
26 | time.Now(),
27 | "id",
28 | id,
29 | )
30 | return sql
31 | }
32 |
33 | func (b *MySQLBuilder) BuildDeletePovitSQL(povit *data.AssociationPovit) string {
34 | return fmt.Sprintf(
35 | "DELETE FROM `%s` WHERE (`%s` = %d AND `%s` = %d)",
36 | povit.Table().Name,
37 | povit.Source.Column.Name,
38 | povit.Source.Value,
39 | povit.Target.Column.Name,
40 | povit.Target.Value,
41 | )
42 | }
43 |
44 | func (b *MySQLBuilder) BuildClearAssociationSQL(ownerId uint64, tableName string, ownerFieldName string) string {
45 | sql := fmt.Sprintf(
46 | "DELETE FROM `%s` WHERE (`%s` = '%d')",
47 | tableName,
48 | ownerFieldName,
49 | ownerId,
50 | )
51 | return sql
52 | }
53 |
--------------------------------------------------------------------------------
/db/dialect/mysql/insert.go:
--------------------------------------------------------------------------------
1 | package mysql
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 |
7 | "rxdrag.com/entify/model/data"
8 | "rxdrag.com/entify/model/table"
9 | )
10 |
11 | func (b *MySQLBuilder) BuildInsertSQL(fields []*data.Field, table *table.Table) string {
12 | sql := fmt.Sprintf("INSERT INTO `%s`(%s) VALUES(%s)", table.Name, insertFields(fields), insertValueSymbols(fields))
13 |
14 | return sql
15 | }
16 |
17 | func (b *MySQLBuilder) BuildInsertPovitSQL(povit *data.AssociationPovit) string {
18 | return fmt.Sprintf(
19 | "INSERT INTO `%s`(%s,%s) VALUES(%d, %d)",
20 | povit.Table().Name,
21 | povit.Source.Column.Name,
22 | povit.Target.Column.Name,
23 | povit.Source.Value,
24 | povit.Target.Value,
25 | )
26 | }
27 |
28 | func insertFields(fields []*data.Field) string {
29 | names := make([]string, len(fields))
30 | for i := range fields {
31 | names[i] = fields[i].Column.Name
32 | }
33 | return strings.Join(names, ",")
34 | }
35 |
36 | func insertValueSymbols(fields []*data.Field) string {
37 | array := make([]string, len(fields))
38 | for i := range array {
39 | array[i] = "?"
40 | }
41 | return strings.Join(array, ",")
42 | }
43 |
--------------------------------------------------------------------------------
/db/dialect/mysql/query_test.go:
--------------------------------------------------------------------------------
1 | package mysql
2 |
3 | import (
4 | "testing"
5 |
6 | "rxdrag.com/entify/model"
7 | "rxdrag.com/entify/model/table"
8 | )
9 |
10 | func TestModifyTableName(t *testing.T) {
11 | var mysqlBuilder MySQLBuilder
12 |
13 | atoms := mysqlBuilder.BuildModifyTableAtoms(
14 | &model.TableDiff{
15 | OldTable: &table.Table{
16 | Name: "User",
17 | },
18 | NewTable: &table.Table{
19 | Name: "User2",
20 | },
21 | },
22 | )
23 |
24 | if len(atoms) != 1 {
25 | t.Error("Modify atoms number error")
26 | }
27 |
28 | if atoms[0].ExcuteSQL != "ALTER TABLE User RENAME TO User2 " {
29 | t.Error("Modify atom ExcuteSQL error:#" + atoms[0].ExcuteSQL + "#")
30 | }
31 |
32 | if atoms[0].UndoSQL != "ALTER TABLE User2 RENAME TO User " {
33 | t.Error("Modify atom UndoSQL error:#" + atoms[0].UndoSQL + "#")
34 | }
35 | }
36 |
37 | func TestModifyColumnName(t *testing.T) {
38 | var mysqlBuilder MySQLBuilder
39 | atoms := mysqlBuilder.BuildModifyTableAtoms(
40 | &model.TableDiff{
41 | OldTable: &table.Table{
42 | Uuid: "uuid1",
43 | Name: "User",
44 | },
45 | NewTable: &table.Table{
46 | Uuid: "uuid1",
47 | Name: "User",
48 | },
49 | },
50 | )
51 |
52 | if len(atoms) != 1 {
53 | t.Errorf("Modify atoms number error, number:%d", len(atoms))
54 | }
55 |
56 | if atoms[0].ExcuteSQL != "ALTER TABLE User CHANGE COLUMN newColumn1 nickname text" {
57 | t.Errorf("ExcuteSQL error:" + atoms[0].ExcuteSQL)
58 | }
59 |
60 | if atoms[0].UndoSQL != "ALTER TABLE User CHANGE COLUMN nickname newColumn1 text" {
61 | t.Errorf("UndoSQL error:" + atoms[0].UndoSQL)
62 | }
63 | t.Log("#" + atoms[0].ExcuteSQL + "#")
64 | t.Log(atoms[0].UndoSQL)
65 | }
66 |
--------------------------------------------------------------------------------
/db/dialect/mysql/update.go:
--------------------------------------------------------------------------------
1 | package mysql
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 |
7 | "rxdrag.com/entify/model/data"
8 | "rxdrag.com/entify/model/table"
9 | )
10 |
11 | func (b *MySQLBuilder) BuildUpdateSQL(id uint64, fields []*data.Field, table *table.Table) string {
12 | sql := fmt.Sprintf(
13 | "UPDATE `%s` SET %s WHERE ID = %d",
14 | table.Name,
15 | updateSetFields(fields),
16 | id,
17 | )
18 |
19 | return sql
20 | }
21 |
22 | func updateSetFields(fields []*data.Field) string {
23 | if len(fields) == 0 {
24 | panic("No update fields")
25 | }
26 | newKeys := make([]string, len(fields))
27 | for i, field := range fields {
28 | newKeys[i] = field.Column.Name + "=?"
29 | }
30 | return strings.Join(newKeys, ",")
31 | }
32 |
--------------------------------------------------------------------------------
/db/nulluint64.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "database/sql"
5 | "database/sql/driver"
6 | )
7 |
8 | var (
9 | _ sql.Scanner = (*NullUint64)(nil)
10 | _ driver.Value = (*NullUint64)(nil)
11 | )
12 |
13 | // NullUint64 represents an uint64 that may be null.
14 | // NullUint64 implements the Scanner interface so
15 | // it can be used as a scan destination, similar to NullString.
16 | type NullUint64 struct {
17 | Uint64 uint64
18 | Valid bool // Valid is true if Uint64 is not NULL
19 | i struct {
20 | sql.NullInt64
21 | }
22 | }
23 |
24 | // Scan implements the Scanner interface.
25 | func (n *NullUint64) Scan(value interface{}) error {
26 | if err := n.i.NullInt64.Scan(value); err != nil {
27 | return err
28 | }
29 | n.Uint64 = uint64(n.i.NullInt64.Int64)
30 | n.Valid = n.i.NullInt64.Valid
31 | return nil
32 | }
33 |
34 | // Value implements the driver Valuer interface.
35 | func (n NullUint64) Value() (driver.Value, error) {
36 | if !n.Valid {
37 | return nil, nil
38 | }
39 | return int64(n.Uint64), nil
40 | }
41 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module rxdrag.com/entify
2 |
3 | go 1.18
4 |
5 | require (
6 | github.com/dgrijalva/jwt-go v3.2.0+incompatible
7 | github.com/go-sql-driver/mysql v1.6.0
8 | github.com/google/uuid v1.3.0
9 | github.com/gorilla/websocket v1.5.0
10 | github.com/graph-gophers/dataloader v5.0.0+incompatible
11 | github.com/graphql-go/graphql v0.8.0
12 | github.com/mitchellh/mapstructure v1.4.3
13 | github.com/spf13/viper v1.11.0
14 | golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9
15 | )
16 |
17 | require (
18 | github.com/fsnotify/fsnotify v1.5.1 // indirect
19 | github.com/hashicorp/hcl v1.0.0 // indirect
20 | github.com/magiconair/properties v1.8.6 // indirect
21 | github.com/opentracing/opentracing-go v1.2.0 // indirect
22 | github.com/pelletier/go-toml v1.9.4 // indirect
23 | github.com/pelletier/go-toml/v2 v2.0.0-beta.8 // indirect
24 | github.com/spf13/afero v1.8.2 // indirect
25 | github.com/spf13/cast v1.4.1 // indirect
26 | github.com/spf13/jwalterweatherman v1.1.0 // indirect
27 | github.com/spf13/pflag v1.0.5 // indirect
28 | github.com/subosito/gotenv v1.2.0 // indirect
29 | golang.org/x/net v0.0.0-20220412020605-290c469a71a5 // indirect
30 | golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
31 | golang.org/x/text v0.3.7 // indirect
32 | gopkg.in/ini.v1 v1.66.4 // indirect
33 | gopkg.in/yaml.v2 v2.4.0 // indirect
34 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
35 | )
36 |
--------------------------------------------------------------------------------
/handler/README.md:
--------------------------------------------------------------------------------
1 | # graphql-go-handler
2 |
3 | Fork of [https://github.com/bhoriuchi/graphql-go-tools](https://github.com/bhoriuchi/graphql-go-tools) with some changes
4 |
5 | ### Usage
6 |
7 | ```go
8 | func main() {
9 | schema, _ := graphql.NewSchema(...)
10 |
11 | h := handler.New(&handler.Config{
12 | Schema: &schema,
13 | Pretty: true,
14 | GraphiQL: handler.NewDefaultGraphiQLConfig(),
15 | })
16 |
17 | http.Handle("/graphql", h)
18 | http.ListenAndServe(":8080", nil)
19 | }
20 | ```
21 |
22 | ### Using Playground
23 | ```go
24 | h := handler.New(&handler.Config{
25 | Schema: &schema,
26 | Pretty: true,
27 | Playground: handler.NewDefaultPlaygroundConfig(),,
28 | })
29 | ```
30 |
31 | ### Details
32 |
33 | The handler will accept requests with
34 | the parameters:
35 |
36 | * **`query`**: A string GraphQL document to be executed.
37 |
38 | * **`variables`**: The runtime values to use for any GraphQL query variables
39 | as a JSON object.
40 |
41 | * **`operationName`**: If the provided `query` contains multiple named
42 | operations, this specifies which operation should be executed. If not
43 | provided, an 400 error will be returned if the `query` contains multiple
44 | named operations.
45 |
46 | GraphQL will first look for each parameter in the URL's query-string:
47 |
48 | ```
49 | /graphql?query=query+getUser($id:ID){user(id:$id){name}}&variables={"id":"4"}
50 | ```
51 |
52 | If not found in the query-string, it will look in the POST request body.
53 | The `handler` will interpret it
54 | depending on the provided `Content-Type` header.
55 |
56 | * **`application/json`**: the POST body will be parsed as a JSON
57 | object of parameters.
58 |
59 | * **`application/x-www-form-urlencoded`**: this POST body will be
60 | parsed as a url-encoded string of key-value pairs.
61 |
62 | * **`application/graphql`**: The POST body will be parsed as GraphQL
63 | query string, which provides the `query` parameter.
64 |
--------------------------------------------------------------------------------
/handler/handlerfunc.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "log"
7 | "net/http"
8 | "sync"
9 |
10 | "github.com/google/uuid"
11 | "github.com/gorilla/websocket"
12 | "github.com/graphql-go/graphql"
13 | )
14 |
15 | var upgrader = websocket.Upgrader{
16 | ReadBufferSize: 1024,
17 | WriteBufferSize: 1024,
18 | CheckOrigin: func(r *http.Request) bool {
19 | return true
20 | },
21 | Subprotocols: []string{"graphql-ws"},
22 | }
23 |
24 | type ConnectionACKMessage struct {
25 | OperationID string `json:"id,omitempty"`
26 | Type string `json:"type"`
27 | Payload struct {
28 | Query string `json:"query"`
29 | } `json:"payload,omitempty"`
30 | }
31 |
32 | func NewFunc(schemaResolveFn SchemaResolveFunc) func(w http.ResponseWriter, r *http.Request) {
33 | return func(w http.ResponseWriter, r *http.Request) {
34 | conn, err := upgrader.Upgrade(w, r, nil)
35 | if err != nil {
36 | log.Printf("failed to do websocket upgrade: %v", err)
37 | w.WriteHeader(http.StatusInternalServerError)
38 | return
39 | }
40 |
41 | connectionACK, err := json.Marshal(map[string]string{
42 | "type": "connection_ack",
43 | })
44 | if err != nil {
45 | log.Printf("failed to marshal ws connection ack: %v", err)
46 | w.WriteHeader(http.StatusInternalServerError)
47 | return
48 | }
49 |
50 | if err := conn.WriteMessage(websocket.TextMessage, connectionACK); err != nil {
51 | log.Printf("failed to write to ws connection: %v", err)
52 | w.WriteHeader(http.StatusInternalServerError)
53 | return
54 | }
55 | go handleSubscription(conn, schemaResolveFn)
56 | }
57 | }
58 |
59 | func handleSubscription(conn *websocket.Conn, schemaResolveFn SchemaResolveFunc) {
60 | var subscriber *Subscriber
61 | subscriptionCtx, subscriptionCancelFn := context.WithCancel(context.Background())
62 |
63 | handleClosedConnection := func() {
64 | log.Println("[SubscriptionsHandler] subscriber closed connection")
65 | unsubscribe(subscriptionCancelFn, subscriber)
66 | return
67 | }
68 |
69 | for {
70 | _, p, err := conn.ReadMessage()
71 | if err != nil {
72 | log.Printf("failed to read websocket message: %v", err)
73 | return
74 | }
75 |
76 | var msg ConnectionACKMessage
77 | if err := json.Unmarshal(p, &msg); err != nil {
78 | log.Printf("failed to unmarshal websocket message: %v", err)
79 | continue
80 | }
81 |
82 | if msg.Type == "stop" {
83 | handleClosedConnection()
84 | return
85 | }
86 |
87 | if msg.Type == "start" {
88 | subscriber = subscribe(subscriptionCtx, subscriptionCancelFn, conn, msg, schemaResolveFn)
89 | }
90 | }
91 | }
92 |
93 | type Subscriber struct {
94 | UUID string
95 | Conn *websocket.Conn
96 | RequestString string
97 | OperationID string
98 | }
99 |
100 | var subscribers sync.Map
101 |
102 | func subscribersSize() uint64 {
103 | var size uint64
104 | subscribers.Range(func(_, _ interface{}) bool {
105 | size++
106 | return true
107 | })
108 | return size
109 | }
110 |
111 | func unsubscribe(subscriptionCancelFn context.CancelFunc, subscriber *Subscriber) {
112 | subscriptionCancelFn()
113 | if subscriber != nil {
114 | subscriber.Conn.Close()
115 | subscribers.Delete(subscriber.UUID)
116 | }
117 | log.Printf("[SubscriptionsHandler] subscribers size: %+v", subscribersSize())
118 | }
119 |
120 | func subscribe(ctx context.Context,
121 | subscriptionCancelFn context.CancelFunc,
122 | conn *websocket.Conn, msg ConnectionACKMessage,
123 | schemaResolveFn SchemaResolveFunc,
124 | ) *Subscriber {
125 | subscriber := &Subscriber{
126 | UUID: uuid.New().String(),
127 | Conn: conn,
128 | RequestString: msg.Payload.Query,
129 | OperationID: msg.OperationID,
130 | }
131 | subscribers.Store(subscriber.UUID, &subscriber)
132 |
133 | log.Printf("[SubscriptionsHandler] subscribers size: %+v", subscribersSize())
134 |
135 | sendMessage := func(r *graphql.Result) error {
136 | message, err := json.Marshal(map[string]interface{}{
137 | "type": "data",
138 | "id": subscriber.OperationID,
139 | "payload": r.Data,
140 | })
141 | if err != nil {
142 | return err
143 | }
144 |
145 | if err := subscriber.Conn.WriteMessage(websocket.TextMessage, message); err != nil {
146 | return err
147 | }
148 |
149 | return nil
150 | }
151 |
152 | go func() {
153 | subscribeParams := graphql.Params{
154 | Context: ctx,
155 | RequestString: msg.Payload.Query,
156 | Schema: *schemaResolveFn(),
157 | }
158 |
159 | subscribeChannel := graphql.Subscribe(subscribeParams)
160 |
161 | for {
162 | select {
163 | case <-ctx.Done():
164 | log.Printf("[SubscriptionsHandler] subscription ctx done")
165 | return
166 | case r, isOpen := <-subscribeChannel:
167 | if !isOpen {
168 | log.Printf("[SubscriptionsHandler] subscription channel closed")
169 | unsubscribe(subscriptionCancelFn, subscriber)
170 | return
171 | }
172 | if err := sendMessage(r); err != nil {
173 | if err == websocket.ErrCloseSent {
174 | unsubscribe(subscriptionCancelFn, subscriber)
175 | }
176 | log.Printf("failed to send message: %v", err)
177 | }
178 | }
179 | }
180 | }()
181 |
182 | return subscriber
183 | }
184 |
--------------------------------------------------------------------------------
/handler/playground.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "fmt"
5 | "html/template"
6 | "net/http"
7 | "net/url"
8 | "path"
9 | )
10 |
11 | // PlaygroundConfig playground configuration
12 | type PlaygroundConfig struct {
13 | Endpoint string
14 | SubscriptionEndpoint string
15 | Version string
16 | }
17 |
18 | // NewDefaultPlaygroundConfig creates a new default config
19 | func NewDefaultPlaygroundConfig() *PlaygroundConfig {
20 | return &PlaygroundConfig{
21 | Endpoint: "",
22 | SubscriptionEndpoint: "",
23 | Version: PlaygroundVersion,
24 | }
25 | }
26 |
27 | type playgroundData struct {
28 | PlaygroundVersion string
29 | Endpoint string
30 | SubscriptionEndpoint string
31 | SetTitle bool
32 | }
33 |
34 | // renderPlayground renders the Playground GUI
35 | func renderPlayground(config *PlaygroundConfig, w http.ResponseWriter, r *http.Request) {
36 | t := template.New("Playground")
37 | t, err := t.Parse(graphcoolPlaygroundTemplate)
38 | if err != nil {
39 | http.Error(w, err.Error(), http.StatusInternalServerError)
40 | return
41 | }
42 |
43 | endpoint := r.URL.Path
44 | if config.Endpoint != "" {
45 | endpoint = config.Endpoint
46 | }
47 |
48 | subscriptionPath := path.Join(path.Dir(r.URL.Path), "subscriptions")
49 | subscriptionEndpoint := fmt.Sprintf("ws://%v%s", r.Host, subscriptionPath)
50 | if config.SubscriptionEndpoint != "" {
51 | if _, err := url.ParseRequestURI(config.SubscriptionEndpoint); err == nil {
52 | subscriptionEndpoint = config.SubscriptionEndpoint
53 | } else {
54 | subscriptionEndpoint = path.Join(
55 | fmt.Sprintf("ws://%v", r.Host),
56 | config.SubscriptionEndpoint,
57 | )
58 | }
59 | }
60 |
61 | version := PlaygroundVersion
62 | if config.Version != "" {
63 | version = config.Version
64 | }
65 |
66 | d := playgroundData{
67 | PlaygroundVersion: version,
68 | Endpoint: endpoint,
69 | SubscriptionEndpoint: subscriptionEndpoint,
70 | SetTitle: true,
71 | }
72 | err = t.ExecuteTemplate(w, "index", d)
73 | if err != nil {
74 | http.Error(w, err.Error(), http.StatusInternalServerError)
75 | }
76 |
77 | return
78 | }
79 |
80 | // PlaygroundVersion the default version to use
81 | var PlaygroundVersion = "1.7.20"
82 |
83 | const graphcoolPlaygroundTemplate = `
84 | {{ define "index" }}
85 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | GraphQL Playground
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
134 |

135 |
Loading
136 | GraphQL Playground
137 |
138 |
139 |
147 |
148 |
149 |
150 | {{ end }}
151 | `
152 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 |
7 | _ "github.com/go-sql-driver/mysql"
8 | "rxdrag.com/entify/authentication"
9 | "rxdrag.com/entify/config"
10 | "rxdrag.com/entify/consts"
11 | "rxdrag.com/entify/db"
12 | "rxdrag.com/entify/handler"
13 | "rxdrag.com/entify/repository"
14 | "rxdrag.com/entify/resolve"
15 | "rxdrag.com/entify/schema"
16 | )
17 |
18 | const PORT = 4000
19 |
20 | func checkParams() {
21 | dbConfig := config.GetDbConfig()
22 | if dbConfig.Driver == "" ||
23 | dbConfig.Host == "" ||
24 | dbConfig.Database == "" ||
25 | dbConfig.User == "" ||
26 | dbConfig.Port == "" ||
27 | dbConfig.Password == "" {
28 | panic("Params is not enough, please set")
29 | }
30 | }
31 |
32 | func checkMetaInstall() {
33 | if !repository.IsEntityExists(consts.META_ENTITY_NAME) {
34 | repository.Install()
35 | }
36 | }
37 |
38 | // func checkMediaInstall() {
39 | // if !repository.IsEntityExists(consts.MEDIA_ENTITY_NAME) {
40 | // resolve.InstallMedia()
41 | // }
42 | // }
43 |
44 | func main() {
45 | defer db.Close()
46 | checkParams()
47 | checkMetaInstall()
48 | repository.InitGlobalModel()
49 | repository.LoadModel()
50 | // if config.Storage() != "" {
51 | // checkMediaInstall()
52 | // }
53 | if config.AuthUrl() == "" && !repository.IsEntityExists(consts.META_USER) {
54 | schema.InitAuthInstallSchema()
55 | } else {
56 | schema.InitSchema()
57 | }
58 |
59 | h := handler.New(&handler.Config{
60 | SchemaResolveFn: schema.ResolveSchema,
61 | Pretty: true,
62 | GraphiQLConfig: &handler.GraphiQLConfig{},
63 | })
64 |
65 | http.Handle("/graphql",
66 | authentication.CorsMiddleware(
67 | authentication.AuthMiddleware(
68 | resolve.LoadersMiddleware(h),
69 | ),
70 | ),
71 | )
72 | http.HandleFunc("/subscriptions", handler.NewFunc(schema.ResolveSchema))
73 | if config.Storage() == consts.LOCAL {
74 | fmt.Println(fmt.Sprintf("Running a file server at http://localhost:%d/uploads/", PORT))
75 | http.Handle(consts.UPLOAD_PRIFIX+"/", http.StripPrefix(consts.UPLOAD_PRIFIX, http.FileServer(http.Dir(consts.UPLOAD_PATH))))
76 | }
77 |
78 | fmt.Println(fmt.Sprintf("Running a GraphQL API server at http://localhost:%d/graphql", PORT))
79 | fmt.Println(fmt.Sprintf("Subscriptions endpoint is http://localhost:%d/subscriptions", PORT))
80 | err2 := http.ListenAndServe(fmt.Sprintf(":%d", PORT), nil)
81 | if err2 != nil {
82 | fmt.Printf("启动失败:%s", err2)
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/model/data/instance.go:
--------------------------------------------------------------------------------
1 | package data
2 |
3 | import (
4 | "fmt"
5 | "strconv"
6 | "time"
7 |
8 | "rxdrag.com/entify/consts"
9 | "rxdrag.com/entify/model/graph"
10 | "rxdrag.com/entify/model/table"
11 | )
12 |
13 | type Field struct {
14 | Column *table.Column
15 | Value interface{}
16 | }
17 |
18 | type Instance struct {
19 | Id uint64
20 | Entity *graph.Entity
21 | Fields []*Field
22 | Associations []Associationer
23 | }
24 |
25 | func NewInstance(object map[string]interface{}, entity *graph.Entity) *Instance {
26 | instance := Instance{
27 | Entity: entity,
28 | }
29 | if object[consts.ID] != nil {
30 | instance.Id = parseId(object[consts.ID])
31 | }
32 |
33 | columns := entity.Table.Columns
34 | for i := range columns {
35 | column := columns[i]
36 | if object[column.Name] != nil {
37 | instance.Fields = append(instance.Fields, &Field{
38 | Column: column,
39 | Value: object[column.Name],
40 | })
41 | } else if column.CreateDate || column.UpdateDate {
42 | instance.Fields = append(instance.Fields, &Field{
43 | Column: column,
44 | Value: time.Now(),
45 | })
46 | }
47 | }
48 | allAssociation := entity.AllAssociations()
49 | for i := range allAssociation {
50 | asso := allAssociation[i]
51 | if !asso.IsAbstract() {
52 | value := object[asso.Name()]
53 | if value != nil {
54 | ref := Reference{
55 | Association: asso,
56 | Value: value.(map[string]interface{}),
57 | }
58 | instance.Associations = append(instance.Associations, &ref)
59 | }
60 |
61 | } else {
62 | derivedAssociations := asso.DerivedAssociationsByOwnerUuid(entity.Uuid())
63 | for j := range derivedAssociations {
64 | derivedAsso := derivedAssociations[j]
65 | value := object[derivedAsso.Name()]
66 | if value != nil {
67 | ref := DerivedReference{
68 | Association: derivedAsso,
69 | Value: value.(map[string]interface{}),
70 | }
71 | instance.Associations = append(instance.Associations, &ref)
72 | }
73 | }
74 | }
75 | }
76 | return &instance
77 | }
78 |
79 | func (ins *Instance) IsInsert() bool {
80 | for i := range ins.Fields {
81 | field := ins.Fields[i]
82 | if field.Column.Name == consts.ID {
83 | if field.Value != nil {
84 | return false
85 | }
86 | }
87 | }
88 | return true
89 | }
90 |
91 | func (ins *Instance) Table() *table.Table {
92 | return ins.Entity.Table
93 | }
94 |
95 | func parseId(id interface{}) uint64 {
96 | switch v := id.(type) {
97 | default:
98 | panic(fmt.Sprintf("unexpected id type %T", v))
99 | case uint64:
100 | return id.(uint64)
101 | case string:
102 | u, err := strconv.ParseUint(id.(string), 0, 64)
103 | if err != nil {
104 | panic(err.Error())
105 | }
106 | return u
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/model/data/povit.go:
--------------------------------------------------------------------------------
1 | package data
2 |
3 | import "rxdrag.com/entify/model/table"
4 |
5 | type AssociationPovit struct {
6 | Source *Field
7 | Target *Field
8 | //Fields []*Field
9 | Association Associationer
10 | }
11 |
12 | type DerivedAssociationPovit struct {
13 | Source *Field
14 | Target *Field
15 | //Fields []*Field
16 | DerivedReference *DerivedReference
17 | }
18 |
19 | func NewAssociationPovit(association Associationer, sourceId uint64, targetId uint64) *AssociationPovit {
20 | sourceColumn := association.SourceColumn()
21 | targetColumn := association.TargetColumn()
22 | povit := AssociationPovit{
23 | Association: association,
24 | Source: &Field{
25 | Column: sourceColumn,
26 | Value: sourceId,
27 | },
28 | Target: &Field{
29 | Column: targetColumn,
30 | Value: targetId,
31 | },
32 | }
33 |
34 | return &povit
35 | }
36 |
37 | func (a *AssociationPovit) Table() *table.Table {
38 | return a.Association.Table()
39 | }
40 |
41 | // func NewDerivedAssociationPovit(association Associationer, sourceId uint64, targetId uint64) *AssociationPovit {
42 | // sourceColumn := association.SourceColumn()
43 | // targetColumn := association.TargetColumn()
44 | // povit := DerivedAssociationPovit{
45 | // Association: association,
46 | // source: &Field{
47 | // Column: sourceColumn,
48 | // Value: sourceId,
49 | // },
50 | // target: &Field{
51 | // Column: targetColumn,
52 | // Value: targetId,
53 | // },
54 | // }
55 |
56 | // return &povit
57 | // }
58 |
--------------------------------------------------------------------------------
/model/data/relation.go:
--------------------------------------------------------------------------------
1 | package data
2 |
3 | import (
4 | "rxdrag.com/entify/consts"
5 | "rxdrag.com/entify/model/graph"
6 | "rxdrag.com/entify/model/meta"
7 | "rxdrag.com/entify/model/table"
8 | )
9 |
10 | // type HasOne struct {
11 | // Cascade bool
12 | // Add Instance
13 | // Delete Instance
14 | // Update Instance
15 | // Sync Instance
16 | // }
17 |
18 | // type HasMany struct {
19 | // Cascade bool
20 | // Add []Instance
21 | // Delete []Instance
22 | // Update []Instance
23 | // Sync []Instance
24 | // }
25 |
26 | type Associationer interface {
27 | Deleted() []*Instance
28 | Added() []*Instance
29 | Updated() []*Instance
30 | Synced() []*Instance
31 | Cascade() bool
32 | SourceColumn() *table.Column
33 | TargetColumn() *table.Column
34 | Table() *table.Table
35 | IsSource() bool
36 | OwnerColumn() *table.Column
37 | TypeColumn() *table.Column
38 | //Entity, Partial and External
39 | TypeEntity() *graph.Entity
40 | IsCombination() bool
41 | }
42 |
43 | //没有继承关系的关联
44 | type Reference struct {
45 | Association *graph.Association
46 | Value map[string]interface{}
47 | }
48 |
49 | type DerivedReference struct {
50 | Association *graph.DerivedAssociation
51 | Value map[string]interface{}
52 | }
53 |
54 | func convertToInstances(objects interface{}, entity *graph.Entity) []*Instance {
55 | instances := []*Instance{}
56 | if objects != nil {
57 | objs := objects.([]interface{})
58 | for i := range objs {
59 | instances = append(instances, NewInstance(objs[i].(map[string]interface{}), entity))
60 | }
61 | }
62 | return instances
63 | }
64 |
65 | func (r *Reference) Deleted() []*Instance {
66 | return convertToInstances(r.Value[consts.ARG_DELETE], r.TypeEntity())
67 | }
68 |
69 | func (r *Reference) Added() []*Instance {
70 | return convertToInstances(r.Value[consts.ARG_ADD], r.TypeEntity())
71 | }
72 |
73 | func (r *Reference) Updated() []*Instance {
74 | return convertToInstances(r.Value[consts.ARG_UPDATE], r.TypeEntity())
75 | }
76 |
77 | func (r *Reference) Synced() []*Instance {
78 | return convertToInstances(r.Value[consts.ARG_SYNC], r.TypeEntity())
79 | }
80 |
81 | func (r *Reference) Cascade() bool {
82 | if r.Value[consts.ARG_CASCADE] != nil {
83 | return r.Value[consts.ARG_CASCADE].(bool)
84 | }
85 | return false
86 | }
87 |
88 | func (r *Reference) SourceColumn() *table.Column {
89 | for i := range r.Association.Relation.Table.Columns {
90 | column := r.Association.Relation.Table.Columns[i]
91 | if column.Name == r.Association.Relation.SourceClass().TableName() {
92 | return column
93 | }
94 | }
95 | return nil
96 | }
97 |
98 | func (r *Reference) TargetColumn() *table.Column {
99 | for i := range r.Association.Relation.Table.Columns {
100 | column := r.Association.Relation.Table.Columns[i]
101 | if column.Name == r.Association.Relation.TargetClass().TableName() {
102 | return column
103 | }
104 | }
105 | return nil
106 | }
107 |
108 | func (r *Reference) Table() *table.Table {
109 | return r.Association.Relation.Table
110 | }
111 |
112 | func (r *Reference) IsSource() bool {
113 | return r.Association.IsSource()
114 | }
115 |
116 | func (r *Reference) OwnerColumn() *table.Column {
117 | if r.IsSource() {
118 | return r.SourceColumn()
119 | } else {
120 | return r.TargetColumn()
121 | }
122 | }
123 | func (r *Reference) TypeColumn() *table.Column {
124 | if !r.IsSource() {
125 | return r.SourceColumn()
126 | } else {
127 | return r.TargetColumn()
128 | }
129 | }
130 |
131 | func (r *Reference) TypeEntity() *graph.Entity {
132 | entity := r.Association.TypeEntity()
133 | if entity != nil {
134 | return entity
135 | }
136 |
137 | partial := r.Association.TypePartial()
138 |
139 | if partial != nil {
140 | return &partial.Entity
141 | }
142 |
143 | external := r.Association.TypeExternal()
144 |
145 | if external != nil {
146 | return &external.Entity
147 | }
148 | panic("Can not find reference entity")
149 | }
150 |
151 | func (r *Reference) IsCombination() bool {
152 | return r.IsSource() &&
153 | (r.Association.Relation.RelationType == meta.TWO_WAY_COMBINATION ||
154 | r.Association.Relation.RelationType == meta.ONE_WAY_COMBINATION)
155 | }
156 |
157 | //====derived
158 | func (r *DerivedReference) Deleted() []*Instance {
159 | return convertToInstances(r.Value[consts.ARG_DELETE], r.TypeEntity())
160 | }
161 |
162 | func (r *DerivedReference) Added() []*Instance {
163 | return convertToInstances(r.Value[consts.ARG_ADD], r.TypeEntity())
164 | }
165 |
166 | func (r *DerivedReference) Updated() []*Instance {
167 | return convertToInstances(r.Value[consts.ARG_UPDATE], r.TypeEntity())
168 | }
169 |
170 | func (r *DerivedReference) Synced() []*Instance {
171 | return convertToInstances(r.Value[consts.ARG_SYNC], r.TypeEntity())
172 | }
173 |
174 | func (r *DerivedReference) Cascade() bool {
175 | return r.Value[consts.ARG_CASCADE].(bool)
176 | }
177 |
178 | func (r *DerivedReference) SourceColumn() *table.Column {
179 | for i := range r.Association.Relation.Table.Columns {
180 | column := r.Association.Relation.Table.Columns[i]
181 | if column.Name == r.Association.Relation.SourceClass().TableName() {
182 | return column
183 | }
184 | }
185 | return nil
186 | }
187 |
188 | func (r *DerivedReference) TargetColumn() *table.Column {
189 | for i := range r.Association.Relation.Table.Columns {
190 | column := r.Association.Relation.Table.Columns[i]
191 | if column.Name == r.Association.Relation.TargetClass().TableName() {
192 | return column
193 | }
194 | }
195 | return nil
196 | }
197 |
198 | func (r *DerivedReference) Table() *table.Table {
199 | return r.Association.Relation.Table
200 | }
201 |
202 | func (r *DerivedReference) IsSource() bool {
203 | return r.Association.DerivedFrom.IsSource()
204 | }
205 |
206 | func (r *DerivedReference) OwnerColumn() *table.Column {
207 | if r.IsSource() {
208 | return r.SourceColumn()
209 | } else {
210 | return r.TargetColumn()
211 | }
212 | }
213 | func (r *DerivedReference) TypeColumn() *table.Column {
214 | if !r.IsSource() {
215 | return r.SourceColumn()
216 | } else {
217 | return r.TargetColumn()
218 | }
219 | }
220 |
221 | func (r *DerivedReference) TypeEntity() *graph.Entity {
222 | entity := r.Association.TypeEntity()
223 | if entity != nil {
224 | return entity
225 | }
226 |
227 | partial := r.Association.TypePartial()
228 |
229 | if partial != nil {
230 | return &partial.Entity
231 | }
232 |
233 | external := r.Association.TypeExternal()
234 |
235 | if external != nil {
236 | return &external.Entity
237 | }
238 | panic("Can not find reference entity")
239 | }
240 |
241 | func (r *DerivedReference) IsCombination() bool {
242 | return r.IsSource() &&
243 | (r.Association.Relation.Parent.RelationType == meta.TWO_WAY_COMBINATION ||
244 | r.Association.Relation.Parent.RelationType == meta.ONE_WAY_COMBINATION)
245 | }
246 |
--------------------------------------------------------------------------------
/model/diff.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import "rxdrag.com/entify/model/table"
4 |
5 | type ModifyAtom struct {
6 | ExcuteSQL string
7 | UndoSQL string
8 | }
9 |
10 | type ColumnDiff struct {
11 | OldColumn *table.Column
12 | NewColumn *table.Column
13 | }
14 |
15 | type TableDiff struct {
16 | OldTable *table.Table
17 | NewTable *table.Table
18 | DeleteColumns []*table.Column
19 | AddColumns []*table.Column
20 | ModifyColumns []ColumnDiff //删除列索引,并重建
21 | }
22 |
23 | type Diff struct {
24 | oldContent *Model
25 | newContent *Model
26 |
27 | DeletedTables []*table.Table
28 | AddedTables []*table.Table
29 | ModifiedTables []*TableDiff
30 | }
31 |
32 | func findTable(uuid string, tables []*table.Table) *table.Table {
33 | for i := range tables {
34 | if tables[i].Uuid == uuid {
35 | return tables[i]
36 | }
37 | }
38 | return nil
39 | }
40 |
41 | func findColumn(uuid string, columns []*table.Column) *table.Column {
42 | for _, column := range columns {
43 | if column.Uuid == uuid {
44 | return column
45 | }
46 | }
47 |
48 | return nil
49 | }
50 |
51 | func columnDifferent(oldColumn, newColumn *table.Column) *ColumnDiff {
52 | diff := ColumnDiff{
53 | OldColumn: oldColumn,
54 | NewColumn: newColumn,
55 | }
56 | if oldColumn.Name != newColumn.Name {
57 | return &diff
58 | }
59 | // if oldColumn.Generated != newColumn.Generated {
60 | // return &diff
61 | // }
62 | if oldColumn.Index != newColumn.Index {
63 | return &diff
64 | }
65 | if oldColumn.Nullable != newColumn.Nullable {
66 | return &diff
67 | }
68 | if oldColumn.Length != newColumn.Length {
69 | return &diff
70 | }
71 | if oldColumn.Primary != newColumn.Primary {
72 | return &diff
73 | }
74 |
75 | if oldColumn.Unique != newColumn.Unique {
76 | return &diff
77 | }
78 |
79 | if oldColumn.Type != newColumn.Type {
80 | return &diff
81 | }
82 | return nil
83 | }
84 | func tableDifferent(oldTable, newTable *table.Table) *TableDiff {
85 | var diff TableDiff
86 | modified := false
87 | diff.OldTable = oldTable
88 | diff.NewTable = newTable
89 |
90 | for _, column := range oldTable.Columns {
91 | foundCoumn := findColumn(column.Uuid, newTable.Columns)
92 | if foundCoumn == nil {
93 | diff.DeleteColumns = append(diff.DeleteColumns, column)
94 | modified = true
95 | }
96 | }
97 |
98 | for _, column := range newTable.Columns {
99 | foundColumn := findColumn(column.Uuid, oldTable.Columns)
100 | if foundColumn == nil {
101 | diff.AddColumns = append(diff.AddColumns, column)
102 | modified = true
103 | } else {
104 | columnDiff := columnDifferent(foundColumn, column)
105 | if columnDiff != nil {
106 | diff.ModifyColumns = append(diff.ModifyColumns, *columnDiff)
107 | modified = true
108 | }
109 | }
110 | }
111 |
112 | if diff.OldTable.Name != diff.NewTable.Name || modified {
113 | return &diff
114 | }
115 | return nil
116 | }
117 |
118 | func CreateDiff(published, next *Model) *Diff {
119 | diff := Diff{
120 | oldContent: published,
121 | newContent: next,
122 | }
123 |
124 | publishedTables := published.Graph.Tables
125 | nextTables := next.Graph.Tables
126 |
127 | for _, table := range publishedTables {
128 | foundTable := findTable(table.Uuid, nextTables)
129 | //删除的Table
130 | if foundTable == nil {
131 | diff.DeletedTables = append(diff.DeletedTables, table)
132 | }
133 | }
134 | for _, table := range nextTables {
135 | foundTable := findTable(table.Uuid, publishedTables)
136 | //添加的Table
137 | if foundTable == nil {
138 | diff.AddedTables = append(diff.AddedTables, table)
139 | } else { //修改的Table
140 | tableDiff := tableDifferent(foundTable, table)
141 | if tableDiff != nil {
142 | diff.ModifiedTables = append(diff.ModifiedTables, tableDiff)
143 | }
144 | }
145 | }
146 |
147 | return &diff
148 | }
149 |
--------------------------------------------------------------------------------
/model/domain/attribute.go:
--------------------------------------------------------------------------------
1 | package domain
2 |
3 | import "rxdrag.com/entify/model/meta"
4 |
5 | type Attribute struct {
6 | meta.AttributeMeta
7 | Class *Class
8 | }
9 |
10 | func NewAttribute(a *meta.AttributeMeta, c *Class) *Attribute {
11 | return &Attribute{
12 | AttributeMeta: *a,
13 | Class: c,
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/model/domain/class.go:
--------------------------------------------------------------------------------
1 | package domain
2 |
3 | import "rxdrag.com/entify/model/meta"
4 |
5 | type Class struct {
6 | Uuid string
7 | InnerId uint64
8 | StereoType string
9 | Name string
10 | PartialName string
11 | Description string
12 | Root bool
13 | SoftDelete bool
14 | Attributes []*Attribute
15 | Methods []*Method
16 | parents []*Class
17 | Children []*Class
18 | }
19 |
20 | func NewClass(c *meta.ClassMeta) *Class {
21 | cls := Class{
22 | Uuid: c.Uuid,
23 | InnerId: c.InnerId,
24 | StereoType: c.StereoType,
25 | Name: c.Name,
26 | PartialName: c.PartialName,
27 | Description: c.Description,
28 | Root: c.Root,
29 | SoftDelete: c.SoftDelete,
30 | Attributes: make([]*Attribute, len(c.Attributes)),
31 | Methods: make([]*Method, len(c.Methods)),
32 | parents: []*Class{},
33 | Children: []*Class{},
34 | }
35 |
36 | for i := range c.Attributes {
37 | cls.Attributes[i] = NewAttribute(&c.Attributes[i], &cls)
38 | }
39 |
40 | for i := range c.Methods {
41 | cls.Methods[i] = NewMethod(&c.Methods[i], &cls)
42 | }
43 |
44 | return &cls
45 | }
46 |
47 | func (c *Class) HasChildren() bool {
48 | return len(c.Children) > 0
49 | }
50 |
51 | func (c *Class) AllParents() []*Class {
52 | parents := []*Class{}
53 | for i := range c.parents {
54 | parent := c.parents[i]
55 | parents = append(parents, parent)
56 | parents = append(parents, parent.AllParents()...)
57 | }
58 |
59 | return parents
60 | }
61 |
--------------------------------------------------------------------------------
/model/domain/domain.go:
--------------------------------------------------------------------------------
1 | package domain
2 |
3 | /**
4 | * 在domain层,把有子类的实体,拆分成接口+实体
5 | * 比如A => A + AEntity
6 | */
7 |
8 | import (
9 | "rxdrag.com/entify/model/meta"
10 | )
11 |
12 | type Model struct {
13 | Enums []*Enum
14 | Classes []*Class
15 | Relations []*Relation
16 | }
17 |
18 | func New(m *meta.Model) *Model {
19 | model := Model{}
20 |
21 | for i := range m.Classes {
22 | class := m.Classes[i]
23 | if class.StereoType == meta.CLASSS_ENUM {
24 | model.Enums = append(model.Enums, NewEnum(class))
25 | } else {
26 | model.Classes = append(model.Classes, NewClass(class))
27 | }
28 | }
29 |
30 | for i := range m.Relations {
31 | relation := m.Relations[i]
32 |
33 | src := model.GetClassByUuid(relation.SourceId)
34 | tar := model.GetClassByUuid(relation.TargetId)
35 | if src == nil || tar == nil {
36 | panic("Meta is not integral, can not find class of relation:" + relation.Uuid)
37 | }
38 | if relation.RelationType == meta.INHERIT {
39 | src.parents = append(src.parents, tar)
40 | tar.Children = append(tar.Children, src)
41 | } else {
42 | r := NewRelation(relation, src, tar)
43 | model.Relations = append(model.Relations, r)
44 | }
45 | }
46 |
47 | return &model
48 | }
49 |
50 | func (m *Model) GetClassByUuid(uuid string) *Class {
51 | for i := range m.Classes {
52 | cls := m.Classes[i]
53 | if cls.Uuid == uuid {
54 | return cls
55 | }
56 | }
57 |
58 | return nil
59 | }
60 |
--------------------------------------------------------------------------------
/model/domain/enum.go:
--------------------------------------------------------------------------------
1 | package domain
2 |
3 | import "rxdrag.com/entify/model/meta"
4 |
5 | type Enum struct {
6 | Uuid string
7 | Name string
8 | Values []string
9 | }
10 |
11 | func NewEnum(c *meta.ClassMeta) *Enum {
12 | enum := Enum{
13 | Uuid: c.Uuid,
14 | Name: c.Name,
15 | Values: make([]string, len(c.Attributes)),
16 | }
17 |
18 | for i := range c.Attributes {
19 | enum.Values[i] = c.Attributes[i].Name
20 | }
21 |
22 | return &enum
23 | }
24 |
--------------------------------------------------------------------------------
/model/domain/method.go:
--------------------------------------------------------------------------------
1 | package domain
2 |
3 | import "rxdrag.com/entify/model/meta"
4 |
5 | type Method struct {
6 | meta.MethodMeta
7 | Class *Class
8 | }
9 |
10 | func NewMethod(m *meta.MethodMeta, c *Class) *Method {
11 | return &Method{
12 | MethodMeta: *m,
13 | Class: c,
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/model/domain/relation.go:
--------------------------------------------------------------------------------
1 | package domain
2 |
3 | import "rxdrag.com/entify/model/meta"
4 |
5 | type Relation struct {
6 | Uuid string
7 | InnerId uint64
8 | RelationType string
9 | Source *Class
10 | Target *Class
11 | RoleOfTarget string
12 | RoleOfSource string
13 | DescriptionOnSource string
14 | DescriptionOnTarget string
15 | SourceMutiplicity string
16 | TargetMultiplicity string
17 | EnableAssociaitonClass bool
18 | AssociationClass meta.AssociationClass
19 | }
20 |
21 | func NewRelation(r *meta.RelationMeta, s *Class, t *Class) *Relation {
22 | return &Relation{
23 | Uuid: r.Uuid,
24 | InnerId: r.InnerId,
25 | RelationType: r.RelationType,
26 | Source: s,
27 | Target: t,
28 | RoleOfTarget: r.RoleOfTarget,
29 | RoleOfSource: r.RoleOfSource,
30 | DescriptionOnSource: r.DescriptionOnSource,
31 | DescriptionOnTarget: r.DescriptionOnTarget,
32 | SourceMutiplicity: r.SourceMutiplicity,
33 | TargetMultiplicity: r.TargetMultiplicity,
34 | EnableAssociaitonClass: r.EnableAssociaitonClass,
35 | AssociationClass: r.AssociationClass,
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/model/graph/args.go:
--------------------------------------------------------------------------------
1 | package graph
2 |
3 | import (
4 | "fmt"
5 |
6 | "rxdrag.com/entify/consts"
7 | )
8 |
9 | const PREFIX_T string = "t"
10 |
11 | type QueryArg = map[string]interface{}
12 |
13 | type Ider interface {
14 | CreateId() int
15 | }
16 |
17 | type ArgAssociation struct {
18 | Association *Association
19 | ArgEntities []*ArgEntity
20 | }
21 |
22 | type ArgEntity struct {
23 | Id int
24 | Entity *Entity
25 | Partial *Partial
26 | Associations []*ArgAssociation
27 | ExpressionArgs map[string]interface{}
28 | }
29 |
30 | func argEntitiesFromAssociation(associ *Association, ider Ider) []*ArgEntity {
31 | var argEntities []*ArgEntity
32 | typeInterface := associ.TypeInterface()
33 | typeEntity := associ.TypeEntity()
34 | typePartial := associ.TypePartial()
35 | if typeInterface != nil {
36 | children := typeInterface.Children
37 | for i := range children {
38 | argEntities = append(argEntities, &ArgEntity{
39 | Id: ider.CreateId(),
40 | Entity: children[i],
41 | })
42 | }
43 | } else {
44 | argEntities = append(argEntities, &ArgEntity{
45 | Id: ider.CreateId(),
46 | Entity: typeEntity,
47 | Partial: typePartial,
48 | })
49 | }
50 | return argEntities
51 | }
52 |
53 | func (a *ArgEntity) GetAssociation(name string) *ArgAssociation {
54 | for i := range a.Associations {
55 | if a.Associations[i].Association.Name() == name {
56 | return a.Associations[i]
57 | }
58 | }
59 | panic("Can not find entity association:" + a.Entity.Name() + "." + name)
60 | }
61 |
62 | func (a *ArgEntity) GetWithMakeAssociation(name string, ider Ider) *ArgAssociation {
63 | for i := range a.Associations {
64 | if a.Associations[i].Association.Name() == name {
65 | return a.Associations[i]
66 | }
67 | }
68 | allAssociations := a.Entity.AllAssociations()
69 | for i := range allAssociations {
70 | if allAssociations[i].Name() == name {
71 | asso := &ArgAssociation{
72 | Association: allAssociations[i],
73 | ArgEntities: argEntitiesFromAssociation(allAssociations[i], ider),
74 | }
75 |
76 | a.Associations = append(a.Associations, asso)
77 |
78 | return asso
79 | }
80 | }
81 | panic("Can not find entity association:" + a.Entity.Name() + "." + name)
82 | }
83 |
84 | func (e *ArgEntity) Alise() string {
85 | return fmt.Sprintf("%s%d", PREFIX_T, e.Id)
86 | }
87 |
88 | func (a *ArgAssociation) GetTypeEntity(uuid string) *ArgEntity {
89 | entities := a.ArgEntities
90 | for i := range entities {
91 | if entities[i].Entity.Uuid() == uuid {
92 | return entities[i]
93 | }
94 | }
95 |
96 | panic("Can not find association entity by uuid")
97 | }
98 |
99 | func BuildArgEntity(entity *Entity, where interface{}, ider Ider) *ArgEntity {
100 | rootEntity := &ArgEntity{
101 | Id: ider.CreateId(),
102 | Entity: entity,
103 | }
104 | if where != nil {
105 | if whereMap, ok := where.(QueryArg); ok {
106 | buildWhereEntity(rootEntity, whereMap, ider)
107 | }
108 | }
109 | return rootEntity
110 | }
111 |
112 | func buildWhereEntity(argEntity *ArgEntity, where QueryArg, ider Ider) {
113 | for key, value := range where {
114 | switch key {
115 | case consts.ARG_AND, consts.ARG_NOT, consts.ARG_OR:
116 | if subWhere, ok := value.(QueryArg); ok {
117 | buildWhereEntity(argEntity, subWhere, ider)
118 | }
119 | break
120 | default:
121 | association := argEntity.Entity.GetAssociationByName(key)
122 | if association != nil {
123 | argAssociation := argEntity.GetWithMakeAssociation(key, ider)
124 | if subWhere, ok := value.(QueryArg); ok {
125 | for i := range argAssociation.ArgEntities {
126 | buildWhereEntity(argAssociation.ArgEntities[i], subWhere, ider)
127 | }
128 | }
129 | }
130 | break
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/model/graph/args_test.go:
--------------------------------------------------------------------------------
1 | package graph
2 |
--------------------------------------------------------------------------------
/model/graph/association.go:
--------------------------------------------------------------------------------
1 | package graph
2 |
3 | import (
4 | "rxdrag.com/entify/model/meta"
5 | )
6 |
7 | type Association struct {
8 | Relation *Relation
9 | OwnerClassUuid string
10 | }
11 |
12 | type DerivedAssociation struct {
13 | Relation *DerivedRelation
14 | DerivedFrom *Association
15 | OwnerClassUuid string
16 | }
17 |
18 | func NewAssociation(r *Relation, ownerUuid string) *Association {
19 | return &Association{
20 | Relation: r,
21 | OwnerClassUuid: ownerUuid,
22 | }
23 | }
24 |
25 | func (a *Association) Name() string {
26 | if a.IsSource() {
27 | return a.Relation.RoleOfTarget
28 | } else {
29 | return a.Relation.RoleOfSource
30 | }
31 | }
32 |
33 | func (a *Association) Owner() *Class {
34 | if a.IsSource() {
35 | return a.Relation.SourceClass()
36 | } else {
37 | return a.Relation.TargetClass()
38 | }
39 | }
40 |
41 | func (a *Association) TypeClass() *Class {
42 | if !a.IsSource() {
43 | return a.Relation.SourceClass()
44 | } else {
45 | return a.Relation.TargetClass()
46 | }
47 | }
48 |
49 | func (a *Association) TypeInterface() *Interface {
50 | if !a.IsSource() {
51 | return a.Relation.SourceInterface
52 | } else {
53 | return a.Relation.TargetInterface
54 | }
55 | }
56 |
57 | func (a *Association) TypeEntity() *Entity {
58 | if !a.IsSource() {
59 | return a.Relation.SourceEntity
60 | } else {
61 | return a.Relation.TargetEntity
62 | }
63 | }
64 |
65 | func (a *Association) TypePartial() *Partial {
66 | if !a.IsSource() {
67 | return a.Relation.SourcePartial
68 | } else {
69 | return a.Relation.TargetPartial
70 | }
71 | }
72 |
73 | func (a *Association) TypeExternal() *External {
74 | if !a.IsSource() {
75 | return a.Relation.SourceExternal
76 | } else {
77 | return a.Relation.TargetExternal
78 | }
79 | }
80 |
81 | func (a *Association) Description() string {
82 | if a.IsSource() {
83 | return a.Relation.DescriptionOnTarget
84 | } else {
85 | return a.Relation.DescriptionOnSource
86 | }
87 | }
88 |
89 | func (a *Association) IsArray() bool {
90 | if a.IsSource() {
91 | return a.Relation.TargetMultiplicity == meta.ZERO_MANY
92 | } else {
93 | return a.Relation.SourceMutiplicity == meta.ZERO_MANY
94 | }
95 | }
96 |
97 | func (a *Association) IsSource() bool {
98 | return a.Relation.SourceClass().Uuid() == a.OwnerClassUuid
99 | }
100 |
101 | func (a *Association) IsAbstract() bool {
102 | return len(a.Relation.Children) > 0
103 | }
104 |
105 | func (a *Association) DerivedAssociations() []*DerivedAssociation {
106 | associations := []*DerivedAssociation{}
107 | for i := range a.Relation.Children {
108 | derivedRelation := a.Relation.Children[i]
109 | ownerUuid := derivedRelation.SourceClass().Uuid()
110 | if a.Relation.TargetClass().Uuid() == a.OwnerClassUuid {
111 | ownerUuid = derivedRelation.TargetClass().Uuid()
112 | }
113 | associations = append(associations, &DerivedAssociation{
114 | Relation: derivedRelation,
115 | DerivedFrom: a,
116 | OwnerClassUuid: ownerUuid,
117 | })
118 | }
119 | return associations
120 | }
121 |
122 | func (a *Association) DerivedAssociationsByOwnerUuid(ownerUuid string) []*DerivedAssociation {
123 | associations := []*DerivedAssociation{}
124 | allDerived := a.DerivedAssociations()
125 | for i := range allDerived {
126 | if allDerived[i].OwnerClassUuid == ownerUuid {
127 | associations = append(associations, allDerived[i])
128 | }
129 | }
130 | return associations
131 | }
132 |
133 | func (a *Association) GetName() string {
134 | return a.Name()
135 | }
136 |
137 | func (a *Association) Path() string {
138 | return a.Owner().Domain.Name + "." + a.Name()
139 | }
140 |
141 | //对手实体类
142 | func (d *DerivedAssociation) TypeClass() *Class {
143 | if d.Relation.SourceClass().Uuid() == d.OwnerClassUuid {
144 | return d.Relation.TargetClass()
145 | } else {
146 | return d.Relation.SourceClass()
147 | }
148 |
149 | }
150 |
151 | func (d *DerivedAssociation) Owner() *Class {
152 | if d.Relation.SourceClass().Uuid() == d.OwnerClassUuid {
153 | return d.Relation.SourceClass()
154 | } else {
155 | return d.Relation.TargetClass()
156 | }
157 |
158 | }
159 |
160 | func (d *DerivedAssociation) Name() string {
161 | if d.TypeClass().Uuid() == d.DerivedFrom.TypeClass().Uuid() {
162 | return d.DerivedFrom.Name()
163 | } else {
164 | return d.DerivedFrom.Name() + "For" + d.TypeClass().Name()
165 | }
166 | }
167 |
168 | func (a *DerivedAssociation) TypeEntity() *Entity {
169 | if !a.DerivedFrom.IsSource() {
170 | return a.Relation.SourceEntity
171 | } else {
172 | return a.Relation.TargetEntity
173 | }
174 | }
175 |
176 | func (a *DerivedAssociation) TypePartial() *Partial {
177 | if !a.DerivedFrom.IsSource() {
178 | return a.Relation.SourcePartial
179 | } else {
180 | return a.Relation.TargetPartial
181 | }
182 | }
183 |
184 | func (a *DerivedAssociation) TypeExternal() *External {
185 | if !a.DerivedFrom.IsSource() {
186 | return a.Relation.SourceExternal
187 | } else {
188 | return a.Relation.TargetExternal
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/model/graph/attribute.go:
--------------------------------------------------------------------------------
1 | package graph
2 |
3 | import "rxdrag.com/entify/model/domain"
4 |
5 | type Attribute struct {
6 | domain.Attribute
7 | Class *Class
8 | EumnType *Enum
9 | EnityType *Entity
10 | ValueObjectType *Class
11 | }
12 |
13 | func NewAttribute(a *domain.Attribute, c *Class) *Attribute {
14 | return &Attribute{
15 | Attribute: *a,
16 | Class: c,
17 | }
18 | }
19 |
20 | func (a *Attribute) GetName() string {
21 | return a.Attribute.Name
22 | }
23 |
24 | func (a *Attribute) GetType() string {
25 | return a.Attribute.Type
26 | }
27 | func (a *Attribute) GetEumnType() *Enum {
28 | return a.EumnType
29 | }
30 | func (a *Attribute) GetEnityType() *Entity {
31 | return a.EnityType
32 | }
33 |
--------------------------------------------------------------------------------
/model/graph/class.go:
--------------------------------------------------------------------------------
1 | package graph
2 |
3 | import (
4 | "rxdrag.com/entify/consts"
5 | "rxdrag.com/entify/model/domain"
6 | "rxdrag.com/entify/utils"
7 | )
8 |
9 | type Class struct {
10 | attributes []*Attribute
11 | associations []*Association
12 | methods []*Method
13 | Domain *domain.Class
14 | }
15 |
16 | func NewClass(c *domain.Class) *Class {
17 | cls := Class{
18 | Domain: c,
19 | attributes: make([]*Attribute, len(c.Attributes)),
20 | methods: make([]*Method, len(c.Methods)),
21 | }
22 |
23 | for i := range c.Attributes {
24 | cls.attributes[i] = NewAttribute(c.Attributes[i], &cls)
25 | }
26 |
27 | for i := range c.Methods {
28 | cls.methods[i] = NewMethod(c.Methods[i], &cls)
29 | }
30 |
31 | return &cls
32 | }
33 |
34 | func (c *Class) Uuid() string {
35 | return c.Domain.Uuid
36 | }
37 |
38 | func (c *Class) InnerId() uint64 {
39 | return c.Domain.InnerId
40 | }
41 |
42 | func (c *Class) Name() string {
43 | return c.Domain.Name
44 | }
45 |
46 | func (c *Class) Description() string {
47 | return c.Domain.Description
48 | }
49 |
50 | func (c *Class) AddAssociation(a *Association) {
51 | c.associations = append(c.associations, a)
52 | }
53 |
54 | func (c *Class) TableName() string {
55 | return utils.SnakeString(c.Domain.Name)
56 | }
57 |
58 | // func (c *Class) Attributes() []*Attribute {
59 | // return c.attributes
60 | // }
61 |
62 | // func (c *Class) Associations() []*Association {
63 | // return c.associations
64 | // }
65 |
66 | func (c *Class) MethodsByType(operateType string) []*Method {
67 | methods := []*Method{}
68 | for i := range c.methods {
69 | method := c.methods[i]
70 | if method.Method.OperateType == operateType {
71 | methods = append(methods, method)
72 | }
73 | }
74 |
75 | return methods
76 | }
77 |
78 | func (c *Class) IsSoftDelete() bool {
79 | return c.Domain.SoftDelete
80 | }
81 |
82 | func (c *Class) QueryName() string {
83 | return utils.FirstLower(c.Name())
84 | }
85 |
86 | func (c *Class) QueryOneName() string {
87 | return consts.ONE + utils.FirstUpper(c.Name())
88 | }
89 |
90 | func (c *Class) QueryAggregateName() string {
91 | return utils.FirstLower(c.Name()) + utils.FirstUpper(consts.AGGREGATE)
92 | }
93 |
94 | func (c *Class) DeleteName() string {
95 | return consts.DELETE + utils.FirstUpper(c.Name())
96 | }
97 |
98 | func (c *Class) DeleteByIdName() string {
99 | return consts.DELETE + utils.FirstUpper(c.Name()) + consts.BY_ID
100 | }
101 |
102 | func (c *Class) SetName() string {
103 | return consts.SET + utils.FirstUpper(c.Name())
104 | }
105 |
106 | func (c *Class) UpsertName() string {
107 | return consts.UPSERT + utils.FirstUpper(c.Name())
108 | }
109 |
110 | func (c *Class) UpsertOneName() string {
111 | return consts.UPSERT_ONE + utils.FirstUpper(c.Name())
112 | }
113 |
114 | func (c *Class) AggregateName() string {
115 | return c.Name() + utils.FirstUpper(consts.AGGREGATE)
116 | }
117 |
--------------------------------------------------------------------------------
/model/graph/entity.go:
--------------------------------------------------------------------------------
1 | package graph
2 |
3 | import (
4 | "rxdrag.com/entify/consts"
5 | "rxdrag.com/entify/model/domain"
6 | "rxdrag.com/entify/model/table"
7 | "rxdrag.com/entify/utils"
8 | )
9 |
10 | type Entity struct {
11 | Class
12 | Table *table.Table
13 | Interfaces []*Interface
14 | }
15 |
16 | func NewEntity(c *domain.Class) *Entity {
17 | return &Entity{
18 | Class: *NewClass(c),
19 | }
20 | }
21 |
22 | func (e *Entity) GetHasManyName() string {
23 | return utils.FirstUpper(consts.SET) + e.Name() + consts.HAS_MANY
24 | }
25 |
26 | func (e *Entity) GetHasOneName() string {
27 | return utils.FirstUpper(consts.SET) + e.Name() + consts.HAS_ONE
28 | }
29 |
30 | //有同名接口
31 | func (e *Entity) hasInterfaceWithSameName() bool {
32 | return e.Domain.HasChildren()
33 | }
34 |
35 | //包含继承来的
36 | func (e *Entity) AllAttributes() []*Attribute {
37 | attrs := []*Attribute{}
38 | attrs = append(attrs, e.attributes...)
39 | for i := range e.Interfaces {
40 | for j := range e.Interfaces[i].attributes {
41 | attr := e.Interfaces[i].attributes[j]
42 | if findAttribute(attr.Name, attrs) == nil {
43 | attrs = append(attrs, attr)
44 | }
45 | }
46 | }
47 | return attrs
48 | }
49 |
50 | func (e *Entity) AllMethods() []*Method {
51 | methods := []*Method{}
52 | methods = append(methods, e.methods...)
53 | for i := range e.Interfaces {
54 | for j := range e.Interfaces[i].methods {
55 | method := e.Interfaces[i].methods[j]
56 | if findMethod(method.GetName(), methods) == nil {
57 | methods = append(methods, method)
58 | }
59 | }
60 | }
61 | return methods
62 | }
63 |
64 | //包含继承来的
65 | func (e *Entity) AllAssociations() []*Association {
66 | associas := []*Association{}
67 | associas = append(associas, e.associations...)
68 | for i := range e.Interfaces {
69 | for j := range e.Interfaces[i].associations {
70 | asso := e.Interfaces[i].associations[j]
71 | if findAssociation(asso.Name(), associas) == nil {
72 | associas = append(associas, asso)
73 | }
74 | }
75 | }
76 | return associas
77 | }
78 |
79 | func (e *Entity) GetAssociationByName(name string) *Association {
80 | associations := e.AllAssociations()
81 | for i := range associations {
82 | if associations[i].Name() == name {
83 | return associations[i]
84 | }
85 | }
86 |
87 | return nil
88 | }
89 |
90 | func (e *Entity) IsEmperty() bool {
91 | return len(e.AllAttributes()) < 1 && len(e.AllAssociations()) < 1
92 | }
93 |
94 | func (e *Entity) AllAttributeNames() []string {
95 | names := make([]string, len(e.AllAttributes()))
96 |
97 | for i, attr := range e.AllAttributes() {
98 | names[i] = attr.Name
99 | }
100 |
101 | return names
102 | }
103 |
104 | func (e *Entity) GetAttributeByName(name string) *Attribute {
105 | for _, attr := range e.AllAttributes() {
106 | if attr.Name == name {
107 | return attr
108 | }
109 | }
110 |
111 | return nil
112 | }
113 |
114 | func findAttribute(name string, attrs []*Attribute) *Attribute {
115 | for i := range attrs {
116 | if attrs[i].Name == name {
117 | return attrs[i]
118 | }
119 | }
120 | return nil
121 | }
122 |
123 | func findMethod(name string, methods []*Method) *Method {
124 | for i := range methods {
125 | if methods[i].GetName() == name {
126 | return methods[i]
127 | }
128 | }
129 | return nil
130 | }
131 |
132 | func findAssociation(name string, assos []*Association) *Association {
133 | for i := range assos {
134 | if assos[i].Name() == name {
135 | return assos[i]
136 | }
137 | }
138 | return nil
139 | }
140 |
--------------------------------------------------------------------------------
/model/graph/enum.go:
--------------------------------------------------------------------------------
1 | package graph
2 |
3 | import (
4 | "rxdrag.com/entify/model/domain"
5 | )
6 |
7 | type Enum struct {
8 | domain.Enum
9 | }
10 |
11 | func NewEnum(e *domain.Enum) *Enum {
12 | return &Enum{Enum: *e}
13 | }
14 |
--------------------------------------------------------------------------------
/model/graph/external.go:
--------------------------------------------------------------------------------
1 | package graph
2 |
3 | import "rxdrag.com/entify/model/domain"
4 |
5 | type External struct {
6 | Entity
7 | }
8 |
9 | func NewExternal(c *domain.Class) *External {
10 | return &External{
11 | Entity: Entity{
12 | Class: *NewClass(c),
13 | },
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/model/graph/interface.go:
--------------------------------------------------------------------------------
1 | package graph
2 |
3 | import "rxdrag.com/entify/model/domain"
4 |
5 | type Interface struct {
6 | Class
7 | Children []*Entity
8 | Parents []*Interface
9 | }
10 |
11 | func NewInterface(c *domain.Class) *Interface {
12 | return &Interface{
13 | Class: *NewClass(c),
14 | }
15 | }
16 |
17 | func (f *Interface) IsInterface() bool {
18 | return true
19 | }
20 | func (f *Interface) Interface() *Interface {
21 | return f
22 | }
23 | func (f *Interface) Entity() *Entity {
24 | return nil
25 | }
26 |
27 | func (f *Interface) AllAttributes() []*Attribute {
28 | attrs := []*Attribute{}
29 | attrs = append(attrs, f.attributes...)
30 | for i := range f.Parents {
31 | for j := range f.Parents[i].attributes {
32 | attr := f.Parents[i].attributes[j]
33 | if findAttribute(attr.Name, attrs) == nil {
34 | attrs = append(attrs, attr)
35 | }
36 | }
37 | }
38 | return attrs
39 | }
40 |
41 | func (f *Interface) AllMethods() []*Method {
42 | methods := []*Method{}
43 | methods = append(methods, f.methods...)
44 | for i := range f.Parents {
45 | for j := range f.Parents[i].methods {
46 | method := f.Parents[i].methods[j]
47 | if findMethod(method.GetName(), methods) == nil {
48 | methods = append(methods, method)
49 | }
50 | }
51 | }
52 | return methods
53 | }
54 |
55 | func (f *Interface) AllAssociations() []*Association {
56 | associas := []*Association{}
57 | associas = append(associas, f.associations...)
58 | for i := range f.Parents {
59 | for j := range f.Parents[i].associations {
60 | asso := f.Parents[i].associations[j]
61 | if findAssociation(asso.Name(), associas) == nil {
62 | associas = append(associas, asso)
63 | }
64 | }
65 | }
66 | return associas
67 | }
68 |
69 | func (f *Interface) GetAssociationByName(name string) *Association {
70 | associations := f.AllAssociations()
71 | for i := range associations {
72 | if associations[i].Name() == name {
73 | return associations[i]
74 | }
75 | }
76 |
77 | return nil
78 | }
79 |
80 | func (f *Interface) IsEmperty() bool {
81 | return len(f.AllAttributes()) < 1 && len(f.AllAssociations()) < 1
82 | }
83 |
84 | func (f *Interface) AllAttributeNames() []string {
85 | names := make([]string, len(f.AllAttributes()))
86 |
87 | for i, attr := range f.AllAttributes() {
88 | names[i] = attr.Name
89 | }
90 |
91 | return names
92 | }
93 |
94 | func (f *Interface) GetAttributeByName(name string) *Attribute {
95 | for _, attr := range f.AllAttributes() {
96 | if attr.Name == name {
97 | return attr
98 | }
99 | }
100 |
101 | return nil
102 | }
103 |
--------------------------------------------------------------------------------
/model/graph/method.go:
--------------------------------------------------------------------------------
1 | package graph
2 |
3 | import (
4 | "rxdrag.com/entify/model/domain"
5 | )
6 |
7 | type Method struct {
8 | Method *domain.Method
9 | EumnType *Enum
10 | EnityType *Entity
11 | ValueObjectType *Class
12 | Class *Class
13 | }
14 |
15 | func NewMethod(m *domain.Method, c *Class) *Method {
16 | return &Method{
17 | Method: m,
18 | Class: c,
19 | }
20 | }
21 |
22 | func (m *Method) Uuid() string {
23 | return m.Method.Uuid
24 | }
25 |
26 | func (m *Method) GetName() string {
27 | return m.Method.Name
28 | }
29 |
30 | func (m *Method) GetType() string {
31 | return m.Method.Type
32 | }
33 | func (m *Method) GetEumnType() *Enum {
34 | return m.EumnType
35 | }
36 | func (m *Method) GetEnityType() *Entity {
37 | return m.EnityType
38 | }
39 |
--------------------------------------------------------------------------------
/model/graph/partial.go:
--------------------------------------------------------------------------------
1 | package graph
2 |
3 | import (
4 | "rxdrag.com/entify/consts"
5 | "rxdrag.com/entify/model/domain"
6 | "rxdrag.com/entify/utils"
7 | )
8 |
9 | type Partial struct {
10 | Entity
11 | }
12 |
13 | func NewPartial(c *domain.Class) *Partial {
14 | return &Partial{
15 | Entity: Entity{
16 | Class: *NewClass(c),
17 | },
18 | }
19 | }
20 |
21 | func (p *Partial) NameWithPartial() string {
22 | return p.Domain.Name + utils.FirstUpper(p.Domain.PartialName)
23 | }
24 |
25 | func (p *Partial) QueryName() string {
26 | return utils.FirstLower(p.NameWithPartial())
27 | }
28 |
29 | func (p *Partial) QueryOneName() string {
30 | return consts.ONE + utils.FirstUpper(p.NameWithPartial())
31 | }
32 |
33 | func (p *Partial) QueryAggregateName() string {
34 | return utils.FirstLower(p.NameWithPartial()) + utils.FirstUpper(consts.AGGREGATE)
35 | }
36 |
37 | func (p *Partial) DeleteName() string {
38 | return consts.DELETE + utils.FirstUpper(p.NameWithPartial())
39 | }
40 |
41 | func (p *Partial) DeleteByIdName() string {
42 | return consts.DELETE + utils.FirstUpper(p.NameWithPartial()) + consts.BY_ID
43 | }
44 |
45 | func (p *Partial) SetName() string {
46 | return consts.SET + utils.FirstUpper(p.NameWithPartial())
47 | }
48 |
49 | func (p *Partial) InsertName() string {
50 | return consts.INSERT + utils.FirstUpper(p.NameWithPartial())
51 | }
52 |
53 | func (p *Partial) InsertOneName() string {
54 | return consts.INSERT_ONE + utils.FirstUpper(p.NameWithPartial())
55 | }
56 |
57 | func (p *Partial) UpdateName() string {
58 | return consts.UPDATE + utils.FirstUpper(p.NameWithPartial())
59 | }
60 |
61 | func (p *Partial) UpdateOneName() string {
62 | return consts.UPDATE_ONE + utils.FirstUpper(p.NameWithPartial())
63 | }
64 |
65 | func (p *Partial) AggregateName() string {
66 | return p.NameWithPartial() + utils.FirstUpper(consts.AGGREGATE)
67 | }
68 |
69 | func (p *Partial) GetHasManyName() string {
70 | return utils.FirstUpper(consts.SET) + p.NameWithPartial() + consts.HAS_MANY
71 | }
72 |
73 | func (p *Partial) GetHasOneName() string {
74 | return utils.FirstUpper(consts.SET) + p.NameWithPartial() + consts.HAS_ONE
75 | }
76 |
--------------------------------------------------------------------------------
/model/graph/propertier.go:
--------------------------------------------------------------------------------
1 | package graph
2 |
3 | type Propertier interface {
4 | GetName() string
5 | GetType() string
6 | GetEumnType() *Enum
7 | GetEnityType() *Entity
8 | }
9 |
--------------------------------------------------------------------------------
/model/graph/relation.go:
--------------------------------------------------------------------------------
1 | package graph
2 |
3 | import (
4 | "rxdrag.com/entify/model/domain"
5 | "rxdrag.com/entify/model/meta"
6 | "rxdrag.com/entify/model/table"
7 | )
8 |
9 | type Relation struct {
10 | Uuid string
11 | InnerId uint64
12 | RelationType string
13 | SourceInterface *Interface
14 | TargetInterface *Interface
15 | SourceEntity *Entity
16 | TargetEntity *Entity
17 | SourcePartial *Partial
18 | TargetPartial *Partial
19 | SourceExternal *External
20 | TargetExternal *External
21 | RoleOfTarget string
22 | RoleOfSource string
23 | DescriptionOnSource string
24 | DescriptionOnTarget string
25 | SourceMutiplicity string
26 | TargetMultiplicity string
27 | EnableAssociaitonClass bool
28 | AssociationClass meta.AssociationClass
29 | Children []*DerivedRelation
30 | Table *table.Table
31 | }
32 |
33 | type DerivedRelation struct {
34 | Parent *Relation
35 | SourceEntity *Entity
36 | TargetEntity *Entity
37 | SourcePartial *Partial
38 | TargetPartial *Partial
39 | SourceExternal *External
40 | TargetExternal *External
41 | Table *table.Table
42 | }
43 |
44 | func NewRelation(
45 | r *domain.Relation,
46 | sourceInterface *Interface,
47 | targetInterface *Interface,
48 | sourceEntity *Entity,
49 | targetEntity *Entity,
50 | sourcePartial *Partial,
51 | targetPartial *Partial,
52 | sourceExternal *External,
53 | targetExternal *External,
54 | ) *Relation {
55 | relation := &Relation{
56 | Uuid: r.Uuid,
57 | InnerId: r.InnerId,
58 | RelationType: r.RelationType,
59 | SourceInterface: sourceInterface,
60 | TargetInterface: targetInterface,
61 | SourceEntity: sourceEntity,
62 | TargetEntity: targetEntity,
63 | SourcePartial: sourcePartial,
64 | TargetPartial: targetPartial,
65 | SourceExternal: sourceExternal,
66 | TargetExternal: targetExternal,
67 | RoleOfTarget: r.RoleOfTarget,
68 | RoleOfSource: r.RoleOfSource,
69 | DescriptionOnSource: r.DescriptionOnSource,
70 | DescriptionOnTarget: r.DescriptionOnTarget,
71 | SourceMutiplicity: r.SourceMutiplicity,
72 | TargetMultiplicity: r.TargetMultiplicity,
73 | EnableAssociaitonClass: r.EnableAssociaitonClass,
74 | AssociationClass: r.AssociationClass,
75 | }
76 |
77 | return relation
78 | }
79 |
80 | func (r *Relation) SourceClass() *Class {
81 | if r.SourceInterface != nil {
82 | return &r.SourceInterface.Class
83 | }
84 |
85 | if r.SourceEntity != nil {
86 | return &r.SourceEntity.Class
87 | }
88 |
89 | if r.SourcePartial != nil {
90 | return &r.SourcePartial.Class
91 | }
92 | if r.SourceExternal != nil {
93 | return &r.SourceExternal.Class
94 | }
95 | return nil
96 | }
97 |
98 | func (r *Relation) TargetClass() *Class {
99 | if r.TargetInterface != nil {
100 | return &r.TargetInterface.Class
101 | }
102 |
103 | if r.TargetEntity != nil {
104 | return &r.TargetEntity.Class
105 | }
106 |
107 | if r.TargetPartial != nil {
108 | return &r.TargetPartial.Class
109 | }
110 | if r.TargetExternal != nil {
111 | return &r.TargetExternal.Class
112 | }
113 | return nil
114 | }
115 |
116 | func (r *Relation) IsRealRelation() bool {
117 | if r.SourceInterface != nil || r.TargetInterface != nil {
118 | return false
119 | }
120 |
121 | return true
122 | }
123 |
124 | func (r *DerivedRelation) SourceClass() *Class {
125 |
126 | if r.SourceEntity != nil {
127 | return &r.SourceEntity.Class
128 | }
129 |
130 | if r.SourcePartial != nil {
131 | return &r.SourcePartial.Class
132 | }
133 | if r.SourceExternal != nil {
134 | return &r.SourceExternal.Class
135 | }
136 | return nil
137 | }
138 |
139 | func (r *DerivedRelation) TargetClass() *Class {
140 | if r.TargetEntity != nil {
141 | return &r.TargetEntity.Class
142 | }
143 |
144 | if r.TargetPartial != nil {
145 | return &r.TargetPartial.Class
146 | }
147 | if r.TargetExternal != nil {
148 | return &r.TargetExternal.Class
149 | }
150 | return nil
151 | }
152 |
--------------------------------------------------------------------------------
/model/graph/table.go:
--------------------------------------------------------------------------------
1 | package graph
2 |
3 | import (
4 | "fmt"
5 |
6 | "rxdrag.com/entify/consts"
7 | "rxdrag.com/entify/model/meta"
8 | "rxdrag.com/entify/model/table"
9 | )
10 |
11 | func NewEntityTable(entity *Entity, partial bool) *table.Table {
12 | table := &table.Table{
13 | Uuid: entity.Uuid(),
14 | Name: entity.TableName(),
15 | EntityInnerId: entity.Domain.InnerId,
16 | Partial: false,
17 | }
18 |
19 | allAttrs := entity.AllAttributes()
20 | for i := range allAttrs {
21 | attr := allAttrs[i]
22 | table.Columns = append(table.Columns, NewAttributeColumn(attr, partial))
23 | }
24 |
25 | entity.Table = table
26 | return table
27 | }
28 |
29 | func NewAttributeColumn(attr *Attribute, partial bool) *table.Column {
30 | return &table.Column{
31 | AttributeMeta: attr.AttributeMeta,
32 | PartialId: partial && attr.Name == consts.ID,
33 | }
34 | }
35 |
36 | func NewRelationTables(relation *Relation) []*table.Table {
37 | var tables []*table.Table
38 | name := fmt.Sprintf(
39 | "%s_%d_%d_%d",
40 | consts.PIVOT,
41 | relation.SourceClass().InnerId(),
42 | relation.InnerId,
43 | relation.TargetClass().InnerId(),
44 | )
45 | if relation.IsRealRelation() {
46 | tab := &table.Table{
47 | Uuid: relation.SourceClass().Uuid() + relation.Uuid + relation.TargetClass().Uuid(),
48 | Name: name,
49 | Columns: []*table.Column{
50 | {
51 | AttributeMeta: meta.AttributeMeta{
52 | Type: meta.ID,
53 | Uuid: relation.SourceClass().Uuid() + relation.Uuid,
54 | Name: relation.SourceClass().TableName(),
55 | Index: true,
56 | },
57 | },
58 | {
59 | AttributeMeta: meta.AttributeMeta{
60 | Type: meta.ID,
61 | Uuid: relation.TargetClass().Uuid() + relation.Uuid,
62 | Name: relation.TargetClass().TableName(),
63 | Index: true,
64 | },
65 | },
66 | },
67 | PKString: fmt.Sprintf("%s,%s", relation.SourceClass().TableName(), relation.TargetClass().TableName()),
68 | }
69 | if relation.EnableAssociaitonClass {
70 | for i := range relation.AssociationClass.Attributes {
71 | tab.Columns = append(tab.Columns, &table.Column{
72 | AttributeMeta: relation.AssociationClass.Attributes[i],
73 | })
74 | }
75 | }
76 | relation.Table = tab
77 | tables = append(tables, tab)
78 | } else {
79 | for i := range relation.Children {
80 | derivied := relation.Children[i]
81 | tables = append(tables, NewDerivedRelationTable(derivied))
82 | }
83 | }
84 |
85 | return tables
86 | }
87 |
88 | func NewDerivedRelationTable(derived *DerivedRelation) *table.Table {
89 | name := fmt.Sprintf(
90 | "%s_%d_%d_%d",
91 | consts.PIVOT,
92 | derived.SourceClass().InnerId(),
93 | derived.Parent.InnerId,
94 | derived.TargetClass().InnerId(),
95 | )
96 | tab := &table.Table{
97 | Uuid: derived.SourceClass().Uuid() + derived.Parent.Uuid + derived.TargetClass().Uuid(),
98 | Name: name,
99 | Columns: []*table.Column{
100 | {
101 | AttributeMeta: meta.AttributeMeta{
102 | Type: meta.ID,
103 | Uuid: derived.SourceClass().Uuid() + derived.Parent.Uuid,
104 | Name: derived.SourceClass().TableName(),
105 | Index: true,
106 | },
107 | },
108 | {
109 | AttributeMeta: meta.AttributeMeta{
110 | Type: meta.ID,
111 | Uuid: derived.TargetClass().Uuid() + derived.Parent.Uuid,
112 | Name: derived.TargetClass().TableName(),
113 | Index: true,
114 | },
115 | },
116 | },
117 | PKString: fmt.Sprintf("%s,%s", derived.SourceClass().TableName(), derived.TargetClass().TableName()),
118 | }
119 | if derived.Parent.EnableAssociaitonClass {
120 | for i := range derived.Parent.AssociationClass.Attributes {
121 | tab.Columns = append(tab.Columns, &table.Column{
122 | AttributeMeta: derived.Parent.AssociationClass.Attributes[i],
123 | })
124 | }
125 | }
126 | derived.Table = tab
127 | return tab
128 | }
129 |
--------------------------------------------------------------------------------
/model/meta/attributemeta.go:
--------------------------------------------------------------------------------
1 | package meta
2 |
3 | type AttributeMeta struct {
4 | Uuid string `json:"uuid"`
5 | Type string `json:"type"`
6 | Primary bool `json:"primary"`
7 | Name string `json:"name"`
8 | Nullable bool `json:"nullable"`
9 | Default string `json:"default"`
10 | Unique bool `json:"unique"`
11 | Index bool `json:"index"`
12 | CreateDate bool `json:"createDate"`
13 | UpdateDate bool `json:"updateDate"`
14 | DeleteDate bool `json:"deleteDate"`
15 | Select bool `json:"select"`
16 | Length int `json:"length"`
17 | FloatM int `json:"floatM"` //M digits in total
18 | FloatD int `json:"floatD"` //D digits may be after the decimal point
19 | Unsigned bool `json:"unsigned"`
20 | TypeUuid string `json:"typeUuid"`
21 | Readonly bool `json:"readonly"`
22 | Description string `json:"description"`
23 | TypeLabel string `json:"typeLabel"`
24 | System bool `json:"system"`
25 | }
26 |
--------------------------------------------------------------------------------
/model/meta/classmeta.go:
--------------------------------------------------------------------------------
1 | package meta
2 |
3 | const (
4 | CLASSS_ENTITY string = "Entity"
5 | CLASSS_ENUM string = "Enum"
6 | CLASSS_ABSTRACT string = "Abstract"
7 | CLASS_VALUE_OBJECT string = "ValueObject"
8 | CLASS_EXTERNAL string = "External"
9 | CLASS_PARTIAL string = "Partial"
10 | )
11 |
12 | type ClassMeta struct {
13 | Uuid string `json:"uuid"`
14 | InnerId uint64 `json:"innerId"`
15 | Name string `json:"name"`
16 | PartialName string `json:"partialName"`
17 | StereoType string `json:"stereoType"`
18 | Attributes []AttributeMeta `json:"attributes"`
19 | Methods []MethodMeta `json:"methods"`
20 | Root bool `json:"root"`
21 | Description string `json:"description"`
22 | SoftDelete bool `json:"softDelete"`
23 | System bool `json:"system"`
24 | }
25 |
--------------------------------------------------------------------------------
/model/meta/content.go:
--------------------------------------------------------------------------------
1 | package meta
2 |
3 | type MetaContent struct {
4 | Classes []ClassMeta `json:"entities"`
5 | Relations []RelationMeta `json:"relations"`
6 | Diagrams []interface{} `json:"diagrams"`
7 | X6Nodes []interface{} `json:"x6Nodes"`
8 | X6Edges []interface{} `json:"x6Edges"`
9 | }
10 |
--------------------------------------------------------------------------------
/model/meta/meta.go:
--------------------------------------------------------------------------------
1 | package meta
2 |
3 | type Model struct {
4 | Classes []*ClassMeta
5 | Relations []*RelationMeta
6 | }
7 |
8 | func New(m *MetaContent) *Model {
9 | model := Model{
10 | Classes: make([]*ClassMeta, len(m.Classes)),
11 | Relations: make([]*RelationMeta, len(m.Relations)),
12 | }
13 |
14 | for i := range m.Classes {
15 | model.Classes[i] = &m.Classes[i]
16 | }
17 |
18 | for i := range m.Relations {
19 | model.Relations[i] = &m.Relations[i]
20 | }
21 | return &model
22 | }
23 |
--------------------------------------------------------------------------------
/model/meta/methodmeta.go:
--------------------------------------------------------------------------------
1 | package meta
2 |
3 | const (
4 | SCRIPT string = "script"
5 | CLOUD_FUNCTION string = "cloudFunction"
6 | MICRO_SERVICE string = "microService"
7 |
8 | QUERY string = "query"
9 | MUTATION string = "mutation"
10 | )
11 |
12 | type ArgMeta struct {
13 | Uuid string `json:"uuid"`
14 | Type string `json:"type"`
15 | Name string `json:"name"`
16 | TypeUuid string `json:"typeUuid"`
17 | TypeLabel string `json:"typeLabel"`
18 | }
19 |
20 | type MethodMeta struct {
21 | Uuid string `json:"uuid"`
22 | Name string `json:"name"`
23 | Type string `json:"type"`
24 | TypeUuid string `json:"typeUuid"`
25 | TypeLabel string `json:"typeLabel"`
26 | Args []ArgMeta `json:"args"`
27 | OperateType string `json:"operateType"` //Mutation or Query
28 | ImplementType string `json:"implementType"`
29 | MethodImplements string `json:"methodImplements"`
30 | Description string `json:"description"`
31 | }
32 |
--------------------------------------------------------------------------------
/model/meta/predefined.go:
--------------------------------------------------------------------------------
1 | package meta
2 |
3 | import (
4 | "rxdrag.com/entify/consts"
5 | )
6 |
7 | const (
8 | META_STATUS_PUBLISHED string = "published"
9 | META_STATUS_CANCELLED string = "cancelled"
10 | META_STATUS_MIGRATION_ERROR string = "migrationError"
11 | META_STATUS_ROLLBACK_ERROR string = "rollbackError"
12 | META_STATUS_ENUM_UUID string = "META_STATUS_ENUM_UUID"
13 |
14 | META_ABILITY_TYPE_CREATE string = "create"
15 | META_ABILITY_TYPE_READ string = "read"
16 | META_ABILITY_TYPE_UPDATE string = "update"
17 | META_ABILITY_TYPE_DELETE string = "delete"
18 | META_ABILITY_TYPE_ENUM_UUID string = "META_ABILITY_TYPE_ENUM_UUID"
19 |
20 | META_ENTITY_UUID string = "META_ENTITY_UUID"
21 | )
22 |
23 | var MetaStatusEnum = ClassMeta{
24 | Uuid: META_STATUS_ENUM_UUID,
25 | Name: "MetaStatus",
26 | StereoType: ENUM,
27 | Attributes: []AttributeMeta{
28 | {
29 | Name: META_STATUS_PUBLISHED,
30 | },
31 | {
32 | Name: META_STATUS_CANCELLED,
33 | },
34 | {
35 | Name: META_STATUS_MIGRATION_ERROR,
36 | },
37 | {
38 | Name: META_STATUS_ROLLBACK_ERROR,
39 | },
40 | },
41 | }
42 |
43 | var MetaClass = ClassMeta{
44 | Uuid: META_ENTITY_UUID,
45 | Name: consts.META_ENTITY_NAME,
46 | InnerId: consts.META_INNER_ID,
47 | StereoType: CLASSS_ENTITY,
48 | Root: true,
49 | Attributes: []AttributeMeta{
50 | {
51 | Uuid: "META_COLUMN_ID_UUID",
52 | Type: ID,
53 | Name: consts.META_ID,
54 | },
55 | {
56 | Uuid: "META_COLUMN_CONTENT_UUID",
57 | Type: VALUE_OBJECT,
58 | Name: consts.META_CONTENT,
59 | },
60 | {
61 | Uuid: "META_COLUMN_STATUS_UUID",
62 | Type: ENUM,
63 | Name: consts.META_STATUS,
64 | TypeUuid: META_STATUS_ENUM_UUID,
65 | },
66 | {
67 | Uuid: "META_COLUMN_PUBLISHED_AT_UUID",
68 | Type: DATE,
69 | Name: consts.META_PUBLISHEDAT,
70 | },
71 | {
72 | Uuid: "META_COLUMN_CREATED_AT_UUID",
73 | Type: DATE,
74 | Name: consts.META_CREATEDAT,
75 | },
76 | {
77 | Uuid: "META_COLUMN_UPDATED_AT_UUID",
78 | Type: DATE,
79 | Name: consts.META_UPDATEDAT,
80 | },
81 | },
82 | }
83 |
84 | var EntityAuthSettingsClass = ClassMeta{
85 | Name: "EntityAuthSettings",
86 | Uuid: "META_ENTITY_AUTH_SETTINGS_UUID",
87 | InnerId: consts.ENTITY_AUTH_SETTINGS_INNER_ID,
88 | Root: true,
89 | System: true,
90 | Attributes: []AttributeMeta{
91 | {
92 | Name: consts.ID,
93 | Type: ID,
94 | Uuid: "RX_ENTITY_AUTH_SETTINGS_ID_UUID",
95 | Primary: true,
96 | System: true,
97 | },
98 | {
99 | Name: "entityUuid",
100 | Type: "String",
101 | Uuid: "RX_ENTITY_AUTH_SETTINGS_ENTITY_UUID_UUID",
102 | System: true,
103 | Unique: true,
104 | },
105 | {
106 | Name: "expand",
107 | Type: "Boolean",
108 | Uuid: "RX_ENTITY_AUTH_SETTINGS_EXPAND_UUID",
109 | System: true,
110 | },
111 | },
112 | StereoType: "Entity",
113 | }
114 |
115 | var AbilityTypeEnum = ClassMeta{
116 | Uuid: META_ABILITY_TYPE_ENUM_UUID,
117 | Name: "AbilityType",
118 | StereoType: ENUM,
119 | Attributes: []AttributeMeta{
120 | {
121 | Name: META_ABILITY_TYPE_CREATE,
122 | },
123 | {
124 | Name: META_ABILITY_TYPE_READ,
125 | },
126 | {
127 | Name: META_ABILITY_TYPE_UPDATE,
128 | },
129 | {
130 | Name: META_ABILITY_TYPE_DELETE,
131 | },
132 | },
133 | }
134 |
135 | var AbilityClass = ClassMeta{
136 | Name: "Ability",
137 | Uuid: consts.ABILITY_UUID,
138 | InnerId: consts.Ability_INNER_ID,
139 | Root: true,
140 | System: true,
141 | Attributes: []AttributeMeta{
142 | {
143 | Name: consts.ID,
144 | Type: ID,
145 | Uuid: "RX_ABILITY_ID_UUID",
146 | Primary: true,
147 | System: true,
148 | },
149 | {
150 | Name: "entityUuid",
151 | Type: "String",
152 | Uuid: "RX_ABILITY_ENTITY_UUID_UUID",
153 | System: true,
154 | },
155 | {
156 | Name: "columnUuid",
157 | Type: "String",
158 | Uuid: "RX_ABILITY_COLUMN_UUID_UUID",
159 | System: true,
160 | },
161 | {
162 | Name: "can",
163 | Type: "Boolean",
164 | Uuid: "RX_ABILITY_CAN_UUID",
165 | System: true,
166 | },
167 | {
168 | Name: "expression",
169 | Type: "String",
170 | Uuid: "RX_ABILITY_EXPRESSION_UUID",
171 | Length: 2000,
172 | System: true,
173 | },
174 | {
175 | Name: "abilityType",
176 | Type: ENUM,
177 | Uuid: "RX_ABILITY_ABILITYTYPE_UUID",
178 | System: true,
179 | TypeUuid: META_ABILITY_TYPE_ENUM_UUID,
180 | },
181 | {
182 | Name: "roleId",
183 | Type: ID,
184 | Uuid: "RX_ABILITY_ROLE_ID_UUID",
185 | System: true,
186 | },
187 | },
188 | StereoType: "Entity",
189 | }
190 |
--------------------------------------------------------------------------------
/model/meta/relationmeta.go:
--------------------------------------------------------------------------------
1 | package meta
2 |
3 | const (
4 | INHERIT string = "inherit"
5 | TWO_WAY_ASSOCIATION string = "twoWayAssociation"
6 | TWO_WAY_AGGREGATION string = "twoWayAggregation"
7 | TWO_WAY_COMBINATION string = "twoWayCombination"
8 | ONE_WAY_ASSOCIATION string = "oneWayAssociation"
9 | ONE_WAY_AGGREGATION string = "oneWayAggregation"
10 | ONE_WAY_COMBINATION string = "oneWayCombination"
11 |
12 | ZERO_ONE string = "0..1"
13 | ZERO_MANY string = "0..*"
14 | )
15 |
16 | type AssociationClass struct {
17 | Name string `json:"name"`
18 | Attributes []AttributeMeta `json:"attributes"`
19 | }
20 |
21 | type RelationMeta struct {
22 | Uuid string `json:"uuid"`
23 | InnerId uint64 `json:"innerId"`
24 | RelationType string `json:"relationType"`
25 | SourceId string `json:"sourceId"`
26 | TargetId string `json:"targetId"`
27 | RoleOfTarget string `json:"roleOfTarget"`
28 | RoleOfSource string `json:"roleOfSource"`
29 | DescriptionOnSource string `json:"descriptionOnSource"`
30 | DescriptionOnTarget string `json:"descriptionOnTarget"`
31 | SourceMutiplicity string `json:"sourceMutiplicity"`
32 | TargetMultiplicity string `json:"targetMultiplicity"`
33 | EnableAssociaitonClass bool `json:"enableAssociaitonClass"`
34 | AssociationClass AssociationClass `json:"associationClass"`
35 | System bool `json:"system"`
36 | }
37 |
--------------------------------------------------------------------------------
/model/meta/type.go:
--------------------------------------------------------------------------------
1 | package meta
2 |
3 | import "rxdrag.com/entify/consts"
4 |
5 | const (
6 | ID string = "ID"
7 | INT string = "Int"
8 | FLOAT string = "Float"
9 | BOOLEAN string = "Boolean"
10 | STRING string = "String"
11 | DATE string = "Date"
12 | ENUM string = "Enum"
13 | VALUE_OBJECT string = "ValueObject"
14 | ENTITY string = "Entity"
15 | ID_ARRAY string = "ID[]"
16 | INT_ARRAY string = "Int[]"
17 | FLOAT_ARRAY string = "Float[]"
18 | STRING_ARRAY string = "String[]"
19 | DATE_ARRAY string = "Date[]"
20 | ENUM_ARRAY string = "EnumArray"
21 | VALUE_OBJECT_ARRAY string = "ValueObjectArray"
22 | ENTITY_ARRAY string = "EntityArray"
23 | FILE string = consts.FILE
24 | )
25 |
--------------------------------------------------------------------------------
/model/model.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "github.com/graphql-go/graphql"
5 | "rxdrag.com/entify/model/domain"
6 | "rxdrag.com/entify/model/graph"
7 | "rxdrag.com/entify/model/meta"
8 | )
9 |
10 | type Model struct {
11 | Meta *meta.Model
12 | Domain *domain.Model
13 | Graph *graph.Model
14 | Schema *graphql.Schema
15 | }
16 |
17 | func New(c *meta.MetaContent) *Model {
18 | metaModel := meta.New(c)
19 | domainModel := domain.New(metaModel)
20 | grahpModel := graph.New(domainModel)
21 | model := Model{
22 | Meta: metaModel,
23 | Domain: domainModel,
24 | Graph: grahpModel,
25 | Schema: nil,
26 | }
27 | return &model
28 | }
29 |
30 | var GlobalModel *Model
31 |
--------------------------------------------------------------------------------
/model/table/column.go:
--------------------------------------------------------------------------------
1 | package table
2 |
3 | import "rxdrag.com/entify/model/meta"
4 |
5 | type Column struct {
6 | meta.AttributeMeta
7 | PartialId bool
8 | Key bool
9 | }
10 |
--------------------------------------------------------------------------------
/model/table/table.go:
--------------------------------------------------------------------------------
1 | package table
2 |
3 | type Table struct {
4 | Uuid string
5 | Name string
6 | EntityInnerId uint64
7 | Columns []*Column
8 | PKString string
9 | Partial bool
10 | }
11 |
--------------------------------------------------------------------------------
/repository/authorization.go:
--------------------------------------------------------------------------------
1 | package repository
2 |
3 | import (
4 | "encoding/json"
5 |
6 | "github.com/graphql-go/graphql"
7 | "github.com/mitchellh/mapstructure"
8 | "rxdrag.com/entify/authcontext"
9 | "rxdrag.com/entify/common"
10 | "rxdrag.com/entify/consts"
11 | "rxdrag.com/entify/model"
12 | "rxdrag.com/entify/model/graph"
13 | "rxdrag.com/entify/model/meta"
14 | )
15 |
16 | type AbilityVerifier struct {
17 | me *common.User
18 | RoleIds []string
19 | Abilities []*common.Ability
20 | // expression Key : 从Auth模块返回的结果
21 | QueryUserCache map[string][]common.User
22 | isSupper bool
23 | }
24 |
25 | func NewVerifier() *AbilityVerifier {
26 | verifier := AbilityVerifier{}
27 |
28 | return &verifier
29 | }
30 |
31 | func NewSupperVerifier() *AbilityVerifier {
32 | verifier := AbilityVerifier{isSupper: true}
33 |
34 | return &verifier
35 | }
36 |
37 | func (v *AbilityVerifier) Init(p graphql.ResolveParams, entityUuids []string) {
38 | me := authcontext.ParseContextValues(p).Me
39 | v.me = me
40 | if me != nil {
41 | for i := range me.Roles {
42 | v.RoleIds = append(v.RoleIds, me.Roles[i].Id)
43 | }
44 | } else {
45 | v.RoleIds = append(v.RoleIds, consts.GUEST_ROLE_ID)
46 | }
47 |
48 | v.queryRolesAbilities(entityUuids)
49 | }
50 |
51 | func (v *AbilityVerifier) IsSupper() bool {
52 | if v.isSupper {
53 | return true
54 | }
55 |
56 | if v.me != nil {
57 | return v.me.IsSupper
58 | }
59 |
60 | return false
61 | }
62 |
63 | func (v *AbilityVerifier) IsDemo() bool {
64 | if v.me != nil {
65 | return v.me.IsDemo
66 | }
67 |
68 | return false
69 | }
70 |
71 | func (v *AbilityVerifier) WeaveAuthInArgs(entityUuid string, args interface{}) interface{} {
72 | if v.IsSupper() || v.IsDemo() {
73 | return args
74 | }
75 |
76 | var rootAnd []map[string]interface{}
77 |
78 | if args == nil {
79 | rootAnd = []map[string]interface{}{}
80 | } else {
81 | argsMap := args.(map[string]interface{})
82 | if argsMap[consts.ARG_AND] == nil {
83 | rootAnd = []map[string]interface{}{}
84 | } else {
85 | rootAnd = argsMap[consts.ARG_AND].([]map[string]interface{})
86 | }
87 | }
88 |
89 | // if len(v.Abilities) == 0 && !v.IsSupper() && !v.IsDemo() {
90 | // rootAnd = append(rootAnd, map[string]interface{}{
91 | // consts.ID: map[string]interface{}{
92 | // consts.ARG_EQ: 0,
93 | // },
94 | // })
95 |
96 | // return map[string]interface{}{
97 | // consts.ARG_AND: rootAnd,
98 | // }
99 | // }
100 |
101 | expArg := v.queryEntityArgsMap(entityUuid)
102 | if len(expArg) > 0 {
103 | rootAnd = append(rootAnd, expArg)
104 | }
105 |
106 | if args == nil {
107 | return map[string]interface{}{
108 | consts.ARG_AND: rootAnd,
109 | }
110 | } else {
111 | argsMap := args.(map[string]interface{})
112 | argsMap[consts.ARG_AND] = rootAnd
113 | return argsMap
114 | }
115 | }
116 |
117 | func (v *AbilityVerifier) CanReadEntity(entityUuid string) bool {
118 | if v.IsSupper() || v.IsDemo() {
119 | return true
120 | }
121 |
122 | for _, ability := range v.Abilities {
123 | if ability.EntityUuid == entityUuid &&
124 | ability.ColumnUuid == "" &&
125 | ability.Can &&
126 | ability.AbilityType == meta.META_ABILITY_TYPE_READ {
127 | return true
128 | }
129 | }
130 | return false
131 | }
132 |
133 | func (v *AbilityVerifier) EntityMutationCan(entityData map[string]interface{}) bool {
134 | return false
135 | }
136 |
137 | func (v *AbilityVerifier) FieldCan(entityData map[string]interface{}) bool {
138 | return false
139 | }
140 |
141 | func (v *AbilityVerifier) queryEntityArgsMap(entityUuid string) map[string]interface{} {
142 | expMap := map[string]interface{}{}
143 | queryEntityExpressions := []string{}
144 |
145 | for _, ability := range v.Abilities {
146 | if ability.EntityUuid == entityUuid &&
147 | ability.ColumnUuid == "" &&
148 | ability.Can &&
149 | ability.AbilityType == meta.META_ABILITY_TYPE_READ &&
150 | ability.Expression != "" {
151 | queryEntityExpressions = append(queryEntityExpressions, ability.Expression)
152 | }
153 | }
154 | if len(queryEntityExpressions) > 0 {
155 | expMap[consts.ARG_OR] = expressionArrayToArgs(queryEntityExpressions)
156 | }
157 | return expMap
158 | }
159 |
160 | func expressionToKey(expression string) string {
161 | return ""
162 | }
163 |
164 | func expressionArrayToArgs(expressionArray []string) []map[string]interface{} {
165 | var args []map[string]interface{}
166 | for _, expression := range expressionArray {
167 | args = append(args, expressionToArg(expression))
168 | }
169 | return args
170 | }
171 |
172 | func expressionToArg(expression string) map[string]interface{} {
173 | arg := map[string]interface{}{}
174 | err := json.Unmarshal([]byte(expression), &arg)
175 | if err != nil {
176 | panic("Parse authorization expression error:" + err.Error())
177 | }
178 | return arg
179 | }
180 |
181 | func (v *AbilityVerifier) queryRolesAbilities(entityUuids []string) {
182 | abilities := QueryEntity(model.GlobalModel.Graph.GetEntityByUuid(consts.ABILITY_UUID), graph.QueryArg{
183 | consts.ARG_WHERE: graph.QueryArg{
184 | "roleId": graph.QueryArg{
185 | consts.ARG_IN: v.RoleIds,
186 | },
187 | // "abilityType": QueryArg{
188 | // consts.ARG_EQ: v.AbilityType,
189 | // },
190 | "entityUuid": graph.QueryArg{
191 | consts.ARG_IN: entityUuids,
192 | },
193 | },
194 | }, NewSupperVerifier())
195 |
196 | for _, abilityMap := range abilities {
197 | var ability common.Ability
198 | err := mapstructure.Decode(abilityMap, &ability)
199 | if err != nil {
200 | panic(err.Error())
201 | }
202 | v.Abilities = append(v.Abilities, &ability)
203 | }
204 | }
205 |
--------------------------------------------------------------------------------
/repository/connection.go:
--------------------------------------------------------------------------------
1 | package repository
2 |
3 | import (
4 | "rxdrag.com/entify/config"
5 | "rxdrag.com/entify/db"
6 | )
7 |
8 | type Connection struct {
9 | idSeed int //use for sql join table
10 | Dbx *db.Dbx
11 | v *AbilityVerifier
12 | }
13 |
14 | func openWithConfig(cfg config.DbConfig, v *AbilityVerifier) (*Connection, error) {
15 | dbx, err := db.Open(cfg.Driver, DbString(cfg))
16 | if err != nil {
17 | return nil, err
18 | }
19 | con := Connection{
20 | idSeed: 1,
21 | Dbx: dbx,
22 | v: v,
23 | }
24 | return &con, err
25 | }
26 |
27 | func Open(v *AbilityVerifier) (*Connection, error) {
28 | cfg := config.GetDbConfig()
29 | return openWithConfig(cfg, v)
30 | }
31 |
32 | func (c *Connection) BeginTx() error {
33 | return c.Dbx.BeginTx()
34 | }
35 |
36 | func (c *Connection) Commit() error {
37 | return c.Dbx.Commit()
38 | }
39 |
40 | func (c *Connection) ClearTx() {
41 | c.Dbx.ClearTx()
42 | }
43 |
44 | //use for sql join table
45 | func (c *Connection) CreateId() int {
46 | c.idSeed++
47 | return c.idSeed
48 | }
49 |
--------------------------------------------------------------------------------
/repository/convert.go:
--------------------------------------------------------------------------------
1 | package repository
2 |
3 | import (
4 | "database/sql"
5 | "encoding/json"
6 |
7 | "github.com/mitchellh/mapstructure"
8 | "rxdrag.com/entify/db"
9 | "rxdrag.com/entify/model/data"
10 | "rxdrag.com/entify/model/graph"
11 | "rxdrag.com/entify/model/meta"
12 | "rxdrag.com/entify/storage"
13 | "rxdrag.com/entify/utils"
14 | )
15 |
16 | func makeSaveValues(fields []*data.Field) []interface{} {
17 | objValues := make([]interface{}, 0, len(fields))
18 | for _, field := range fields {
19 | value := field.Value
20 | column := field.Column
21 |
22 | if column.Type == meta.VALUE_OBJECT ||
23 | column.Type == meta.ID_ARRAY ||
24 | column.Type == meta.INT_ARRAY ||
25 | column.Type == meta.FLOAT_ARRAY ||
26 | column.Type == meta.STRING_ARRAY ||
27 | column.Type == meta.DATE_ARRAY ||
28 | column.Type == meta.ENUM_ARRAY ||
29 | column.Type == meta.VALUE_OBJECT_ARRAY ||
30 | column.Type == meta.ENTITY_ARRAY {
31 | jsonString, err := json.Marshal(value)
32 | if err != nil {
33 | panic(err.Error())
34 | }
35 | value = jsonString
36 | } else if column.Type == meta.FILE {
37 | file := value.(storage.File)
38 | jsonString, err := json.Marshal(file.Save())
39 | if err != nil {
40 | panic(err.Error())
41 | }
42 | value = jsonString
43 | }
44 | objValues = append(objValues, value)
45 | }
46 | return objValues
47 | }
48 |
49 | func makeInterfaceQueryValues(intf *graph.Interface) []interface{} {
50 | names := intf.AllAttributeNames()
51 | values := make([]interface{}, len(names))
52 | for i, attrName := range names {
53 | attr := intf.GetAttributeByName(attrName)
54 | values[i] = makeAttributeValue(attr)
55 | }
56 |
57 | return values
58 | }
59 |
60 | func makeEntityQueryValues(ent *graph.Entity) []interface{} {
61 | names := ent.AllAttributeNames()
62 | values := make([]interface{}, len(names))
63 | for i, attrName := range names {
64 | attr := ent.GetAttributeByName(attrName)
65 | values[i] = makeAttributeValue(attr)
66 | }
67 |
68 | return values
69 | }
70 |
71 | func makeAttributeValue(attr *graph.Attribute) interface{} {
72 | switch attr.Type {
73 | case meta.ID:
74 | var value db.NullUint64
75 | return &value
76 | case meta.INT:
77 | var value sql.NullInt64
78 | return &value
79 | case meta.FLOAT:
80 | var value sql.NullFloat64
81 | return &value
82 | case meta.BOOLEAN:
83 | var value sql.NullBool
84 | return &value
85 | case meta.DATE:
86 | var value sql.NullTime
87 | return &value
88 | case meta.CLASS_VALUE_OBJECT,
89 | meta.ID_ARRAY,
90 | meta.INT_ARRAY,
91 | meta.FLOAT_ARRAY,
92 | meta.STRING_ARRAY,
93 | meta.DATE_ARRAY,
94 | meta.ENUM_ARRAY,
95 | meta.VALUE_OBJECT_ARRAY,
96 | meta.ENTITY_ARRAY,
97 | meta.FILE:
98 | var value utils.JSON
99 | return &value
100 | // COLUMN_SIMPLE_ARRAY string = "simpleArray" ##待添加代码
101 | // COLUMN_JSON_ARRAY string = "JsonArray"
102 | default:
103 | var value sql.NullString
104 | return &value
105 | }
106 | }
107 |
108 | func convertValuesToInterface(values []interface{}, intf *graph.Interface) map[string]interface{} {
109 | object := make(map[string]interface{})
110 | names := intf.AllAttributeNames()
111 | for i := range names {
112 | value := values[i]
113 | attrName := names[i]
114 | column := intf.GetAttributeByName(attrName)
115 | object[column.Name] = convertOneColumnValue(column, value)
116 |
117 | }
118 | return object
119 | }
120 |
121 | func convertValuesToEntity(values []interface{}, ent *graph.Entity) map[string]interface{} {
122 | object := make(map[string]interface{})
123 | names := ent.AllAttributeNames()
124 | for i := range names {
125 | value := values[i]
126 | attrName := names[i]
127 | column := ent.GetAttributeByName(attrName)
128 | object[column.Name] = convertOneColumnValue(column, value)
129 | }
130 | return object
131 | }
132 |
133 | func convertOneColumnValue(column *graph.Attribute, value interface{}) interface{} {
134 | switch column.Type {
135 | case meta.ID, meta.INT:
136 | nullValue := value.(*db.NullUint64)
137 | if nullValue.Valid {
138 | return nullValue.Uint64
139 | }
140 | case meta.FLOAT:
141 | nullValue := value.(*sql.NullFloat64)
142 | if nullValue.Valid {
143 | return nullValue.Float64
144 | }
145 | case meta.BOOLEAN:
146 | nullValue := value.(*sql.NullBool)
147 | if nullValue.Valid {
148 | return nullValue.Bool
149 | }
150 | case meta.DATE:
151 | nullValue := value.(*sql.NullTime)
152 | if nullValue.Valid {
153 | return nullValue.Time
154 | }
155 | case meta.VALUE_OBJECT,
156 | meta.ID_ARRAY,
157 | meta.INT_ARRAY,
158 | meta.FLOAT_ARRAY,
159 | meta.STRING_ARRAY,
160 | meta.DATE_ARRAY,
161 | meta.ENUM_ARRAY,
162 | meta.VALUE_OBJECT_ARRAY,
163 | meta.ENTITY_ARRAY:
164 | if value != nil {
165 | return *value.(*utils.JSON)
166 | }
167 | return value
168 | case meta.FILE:
169 | var file storage.FileInfo
170 | if value != nil {
171 | err := mapstructure.Decode(value, &file)
172 | if err != nil {
173 | panic(err.Error())
174 | }
175 | return file
176 | }
177 | default:
178 | nullValue := value.(*sql.NullString)
179 | if nullValue.Valid {
180 | return nullValue.String
181 | }
182 | }
183 |
184 | return nil
185 | }
186 |
--------------------------------------------------------------------------------
/repository/db.go:
--------------------------------------------------------------------------------
1 | package repository
2 |
3 | import (
4 | "fmt"
5 |
6 | "rxdrag.com/entify/config"
7 | )
8 |
9 | func DbString(cfg config.DbConfig) string {
10 | return fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true",
11 | cfg.User,
12 | cfg.Password,
13 | cfg.Host,
14 | cfg.Port,
15 | cfg.Database,
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/repository/metas.go:
--------------------------------------------------------------------------------
1 | package repository
2 |
3 | import (
4 | "github.com/mitchellh/mapstructure"
5 | "rxdrag.com/entify/consts"
6 | "rxdrag.com/entify/model"
7 | "rxdrag.com/entify/model/graph"
8 | "rxdrag.com/entify/model/meta"
9 | "rxdrag.com/entify/utils"
10 | )
11 |
12 | func QueryPublishedMeta() interface{} {
13 | publishedMeta := QueryOneEntity(model.GlobalModel.Graph.GetMetaEntity(), graph.QueryArg{
14 | consts.ARG_WHERE: graph.QueryArg{
15 | consts.META_STATUS: graph.QueryArg{
16 | consts.ARG_EQ: meta.META_STATUS_PUBLISHED,
17 | },
18 | },
19 | }, NewSupperVerifier())
20 |
21 | return publishedMeta
22 | }
23 |
24 | func QueryNextMeta() interface{} {
25 | nextMeta := QueryOneEntity(model.GlobalModel.Graph.GetMetaEntity(), graph.QueryArg{
26 | consts.ARG_WHERE: graph.QueryArg{
27 | consts.META_STATUS: graph.QueryArg{
28 | consts.ARG_ISNULL: true,
29 | },
30 | },
31 | }, NewSupperVerifier())
32 | return nextMeta
33 | }
34 |
35 | func DecodeContent(obj interface{}) *meta.MetaContent {
36 | content := meta.MetaContent{}
37 | if obj != nil {
38 | err := mapstructure.Decode(obj.(utils.Object)[consts.META_CONTENT], &content)
39 | if err != nil {
40 | panic("Decode content failure:" + err.Error())
41 | }
42 | }
43 | return &content
44 | }
45 |
46 | func InitGlobalModel() {
47 | //初始值,用户取meta信息,取完后,换掉该部分内容
48 | initMeta := meta.MetaContent{
49 | Classes: []meta.ClassMeta{
50 | meta.MetaStatusEnum,
51 | meta.MetaClass,
52 | },
53 | }
54 | model.GlobalModel = model.New(&initMeta)
55 | }
56 |
57 | func LoadModel() {
58 | publishedMeta := QueryPublishedMeta()
59 | publishedContent := DecodeContent(publishedMeta)
60 | publishedContent.Classes = append(publishedContent.Classes, meta.MetaStatusEnum)
61 | publishedContent.Classes = append(publishedContent.Classes, meta.MetaClass)
62 |
63 | model.GlobalModel = model.New(publishedContent)
64 | }
65 |
66 | // func init() {
67 | // LoadModel()
68 | // }
69 |
--------------------------------------------------------------------------------
/repository/migrate.go:
--------------------------------------------------------------------------------
1 | package repository
2 |
3 | import (
4 | "fmt"
5 | "log"
6 |
7 | "rxdrag.com/entify/db"
8 | "rxdrag.com/entify/db/dialect"
9 | "rxdrag.com/entify/model"
10 | "rxdrag.com/entify/model/table"
11 | )
12 |
13 | func ExcuteDiff(d *model.Diff) {
14 | var undoList []string
15 | con, err := Open(NewSupperVerifier())
16 | dbx := con.Dbx
17 | if err != nil {
18 | panic("Open db error:" + err.Error())
19 | }
20 |
21 | for _, table := range d.DeletedTables {
22 | err = DeleteTable(table, &undoList, dbx)
23 | if err != nil {
24 | rollback(undoList, dbx)
25 | panic("Delete table error:" + err.Error())
26 | }
27 | }
28 |
29 | for _, table := range d.AddedTables {
30 | err = CreateTable(table, &undoList, dbx)
31 | if err != nil {
32 | rollback(undoList, dbx)
33 | panic("Create table error:" + err.Error())
34 | }
35 | }
36 |
37 | for _, tableDiff := range d.ModifiedTables {
38 | err = ModifyTable(tableDiff, &undoList, dbx)
39 | if err != nil {
40 | rollback(undoList, dbx)
41 | panic("Modify table error:" + err.Error())
42 | }
43 | }
44 | }
45 |
46 | func DeleteTable(table *table.Table, undoList *[]string, dbx *db.Dbx) error {
47 | sqlBuilder := dialect.GetSQLBuilder()
48 | excuteSQL := sqlBuilder.BuildDeleteTableSQL(table)
49 | undoSQL := sqlBuilder.BuildCreateTableSQL(table)
50 | _, err := dbx.Exec(excuteSQL)
51 | if err != nil {
52 | return err
53 | }
54 | *undoList = append(*undoList, undoSQL)
55 | log.Println("Delete Table SQL:", excuteSQL)
56 | return nil
57 | }
58 |
59 | func CreateTable(table *table.Table, undoList *[]string, dbx *db.Dbx) error {
60 | sqlBuilder := dialect.GetSQLBuilder()
61 | excuteSQL := sqlBuilder.BuildCreateTableSQL(table)
62 | undoSQL := sqlBuilder.BuildDeleteTableSQL(table)
63 | _, err := dbx.Exec(excuteSQL)
64 | if err != nil {
65 | return err
66 | }
67 | *undoList = append(*undoList, undoSQL)
68 | log.Println("Add Table SQL:", excuteSQL)
69 |
70 | return nil
71 | }
72 |
73 | func ModifyTable(tableDiff *model.TableDiff, undoList *[]string, dbx *db.Dbx) error {
74 | sqlBuilder := dialect.GetSQLBuilder()
75 | atoms := sqlBuilder.BuildModifyTableAtoms(tableDiff)
76 | for _, atom := range atoms {
77 | _, err := dbx.Exec(atom.ExcuteSQL)
78 | if err != nil {
79 | fmt.Println("Error atom", atom.ExcuteSQL, err.Error())
80 | return err
81 | }
82 | *undoList = append(*undoList, atom.UndoSQL)
83 | }
84 | return nil
85 | }
86 |
87 | func rollback(undoList []string, con *db.Dbx) {
88 | for _, sql := range undoList {
89 | _, err := con.Exec(sql)
90 | if err != nil {
91 | log.Println("Rollback failed:", sql)
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/repository/repository.go:
--------------------------------------------------------------------------------
1 | package repository
2 |
3 | import (
4 | "fmt"
5 |
6 | "rxdrag.com/entify/db/dialect"
7 | "rxdrag.com/entify/model/data"
8 | "rxdrag.com/entify/model/graph"
9 | )
10 |
11 | func QueryInterface(intf *graph.Interface, args graph.QueryArg, v *AbilityVerifier) []InsanceData {
12 | con, err := Open(v)
13 | if err != nil {
14 | panic(err.Error())
15 | }
16 | return con.doQueryInterface(intf, args)
17 | }
18 |
19 | func QueryOneInterface(intf *graph.Interface, args graph.QueryArg, v *AbilityVerifier) interface{} {
20 | con, err := Open(v)
21 | if err != nil {
22 | panic(err.Error())
23 | }
24 | return con.doQueryOneInterface(intf, args)
25 | }
26 |
27 | func QueryEntity(entity *graph.Entity, args graph.QueryArg, v *AbilityVerifier) []InsanceData {
28 | con, err := Open(v)
29 | if err != nil {
30 | panic(err.Error())
31 | }
32 | return con.doQueryEntity(entity, args)
33 | }
34 |
35 | func QueryOneEntity(entity *graph.Entity, args graph.QueryArg, v *AbilityVerifier) interface{} {
36 | con, err := Open(v)
37 | if err != nil {
38 | panic(err.Error())
39 | }
40 | return con.doQueryOneEntity(entity, args)
41 | }
42 |
43 | func SaveOne(instance *data.Instance, v *AbilityVerifier) (interface{}, error) {
44 | con, err := Open(v)
45 | if err != nil {
46 | fmt.Println(err.Error())
47 | return nil, err
48 | }
49 | err = con.BeginTx()
50 | defer con.ClearTx()
51 | if err != nil {
52 | fmt.Println(err.Error())
53 | return nil, err
54 | }
55 |
56 | obj, err := con.doSaveOne(instance)
57 | if err != nil {
58 | fmt.Println(err.Error())
59 | return nil, err
60 | }
61 | err = con.Commit()
62 | if err != nil {
63 | fmt.Println(err.Error())
64 | return nil, err
65 | }
66 | return obj, nil
67 | }
68 |
69 | func InsertOne(instance *data.Instance, v *AbilityVerifier) (interface{}, error) {
70 | con, err := Open(v)
71 | if err != nil {
72 | fmt.Println(err.Error())
73 | return nil, err
74 | }
75 | defer con.ClearTx()
76 | if err != nil {
77 | fmt.Println(err.Error())
78 | return nil, err
79 | }
80 |
81 | obj, err := con.doInsertOne(instance)
82 | if err != nil {
83 | fmt.Println(err.Error())
84 | return nil, err
85 | }
86 | err = con.Commit()
87 | if err != nil {
88 | fmt.Println(err.Error())
89 | return nil, err
90 | }
91 | return obj, nil
92 | }
93 |
94 | func BatchQueryAssociations(
95 | association *graph.Association,
96 | ids []uint64,
97 | args graph.QueryArg,
98 | v *AbilityVerifier,
99 | ) []map[string]interface{} {
100 | con, err := Open(v)
101 | if err != nil {
102 | panic(err.Error())
103 | }
104 | if association.IsAbstract() {
105 | return con.doBatchAbstractRealAssociations(association, ids, args, v)
106 | } else {
107 | return con.doBatchRealAssociations(association, ids, args, v)
108 | }
109 | }
110 |
111 | func IsEntityExists(name string) bool {
112 | con, err := Open(NewSupperVerifier())
113 | if err != nil {
114 | panic(err.Error())
115 | }
116 | return con.doCheckEntity(name)
117 | }
118 |
119 | func Install() error {
120 | sqlBuilder := dialect.GetSQLBuilder()
121 | con, err := Open(NewSupperVerifier())
122 | if err != nil {
123 | fmt.Println(err.Error())
124 | return err
125 | }
126 | err = con.BeginTx()
127 | if err != nil {
128 | fmt.Println(err.Error())
129 | return err
130 | }
131 |
132 | _, err = con.Dbx.Exec(sqlBuilder.BuildCreateMetaSQL())
133 | if err != nil {
134 | fmt.Println(err.Error())
135 | return err
136 | }
137 |
138 | _, err = con.Dbx.Exec(sqlBuilder.BuildCreateAbilitySQL())
139 | if err != nil {
140 | fmt.Println(err.Error())
141 | return err
142 | }
143 |
144 | _, err = con.Dbx.Exec(sqlBuilder.BuildCreateEntityAuthSettingsSQL())
145 | if err != nil {
146 | fmt.Println(err.Error())
147 | return err
148 | }
149 |
150 | err = con.Commit()
151 |
152 | if err != nil {
153 | fmt.Println(err.Error())
154 | return err
155 | }
156 |
157 | return nil
158 | }
159 |
--------------------------------------------------------------------------------
/resolve/convertid.go:
--------------------------------------------------------------------------------
1 | package resolve
2 |
3 | import (
4 | "strconv"
5 |
6 | "rxdrag.com/entify/consts"
7 | )
8 |
9 | func ConvertId(object map[string]interface{}) map[string]interface{} {
10 | if object[consts.ID] == nil {
11 | return object
12 | }
13 | switch object[consts.ID].(type) {
14 | case string:
15 | id, err := strconv.ParseUint(object[consts.ID].(string), 10, 64)
16 | if err != nil {
17 | panic("Convert id error:" + err.Error())
18 | }
19 |
20 | object[consts.ID] = id
21 | }
22 |
23 | return object
24 | }
25 |
--------------------------------------------------------------------------------
/resolve/file.go:
--------------------------------------------------------------------------------
1 | package resolve
2 |
3 | import (
4 | "github.com/graphql-go/graphql"
5 | "rxdrag.com/entify/config"
6 | "rxdrag.com/entify/consts"
7 | "rxdrag.com/entify/storage"
8 | "rxdrag.com/entify/utils"
9 | )
10 |
11 | func FileUrlResolve(p graphql.ResolveParams) (interface{}, error) {
12 | defer utils.PrintErrorStack()
13 | if p.Source != nil {
14 | fileInfo := p.Source.(storage.FileInfo)
15 | if config.Storage() == consts.LOCAL {
16 | return p.Context.Value(consts.HOST).(string) + consts.UPLOAD_PRIFIX + "/" + fileInfo.Path, nil
17 | } else {
18 | return fileInfo.Path, nil
19 | }
20 | }
21 | return nil, nil
22 | }
23 |
--------------------------------------------------------------------------------
/resolve/loaders.go:
--------------------------------------------------------------------------------
1 | package resolve
2 |
3 | import (
4 | "context"
5 | "fmt"
6 |
7 | "github.com/graph-gophers/dataloader"
8 | "github.com/graphql-go/graphql"
9 | "rxdrag.com/entify/consts"
10 | "rxdrag.com/entify/model/graph"
11 | "rxdrag.com/entify/repository"
12 | "rxdrag.com/entify/utils"
13 | )
14 |
15 | type ResolverKey struct {
16 | id uint64
17 | }
18 |
19 | func NewKey(id uint64) *ResolverKey {
20 | return &ResolverKey{
21 | id: id,
22 | }
23 | }
24 |
25 | func (rk *ResolverKey) String() string {
26 | return fmt.Sprintf("%d", rk.id)
27 | }
28 |
29 | func (rk *ResolverKey) Raw() interface{} {
30 | return rk.id
31 | }
32 |
33 | type Loaders struct {
34 | loaders map[string]*dataloader.Loader
35 | }
36 |
37 | func CreateDataLoaders() *Loaders {
38 | return &Loaders{
39 | loaders: make(map[string]*dataloader.Loader, 1),
40 | }
41 | }
42 |
43 | func (l *Loaders) GetLoader(p graphql.ResolveParams, association *graph.Association, args graph.QueryArg) *dataloader.Loader {
44 | if l.loaders[association.Path()] == nil {
45 | l.loaders[association.Path()] = dataloader.NewBatchedLoader(QueryBatchFn(p, association, args))
46 | }
47 | return l.loaders[association.Path()]
48 | }
49 |
50 | func QueryBatchFn(p graphql.ResolveParams, association *graph.Association, args graph.QueryArg) dataloader.BatchFunc {
51 | return func(ctx context.Context, keys dataloader.Keys) []*dataloader.Result {
52 | defer utils.PrintErrorStack()
53 | v := makeAssociAbilityVerifier(p, association)
54 | results := make([]*dataloader.Result, len(keys))
55 | ids := make([]uint64, len(keys))
56 | for i := range ids {
57 | ids[i] = keys[i].Raw().(uint64)
58 | }
59 | instances := repository.BatchQueryAssociations(association, ids, args, v)
60 |
61 | for i := range results {
62 | var data interface{}
63 | associationInstances := findInstanceFromArray(ids[i], instances)
64 | if !association.IsArray() {
65 | ln := len(associationInstances)
66 | if ln > 1 {
67 | panic(fmt.Sprintf("To many values for %s : %d", association.Owner().Domain.Name+"."+association.Name(), len(associationInstances)))
68 | } else if ln == 1 {
69 | data = associationInstances[0]
70 | } else {
71 | data = nil
72 | }
73 | } else {
74 | data = associationInstances
75 | }
76 | results[i] = &dataloader.Result{
77 | Data: data,
78 | }
79 | }
80 | return results
81 | }
82 | }
83 |
84 | func findInstanceFromArray(id uint64, array []map[string]interface{}) []interface{} {
85 | var instances []interface{}
86 | for i, obj := range array {
87 | if obj[consts.ASSOCIATION_OWNER_ID] == id {
88 | instances = append(instances, array[i])
89 | }
90 | }
91 | return instances
92 | }
93 |
--------------------------------------------------------------------------------
/resolve/logout.go:
--------------------------------------------------------------------------------
1 | package resolve
2 |
3 | import (
4 | "github.com/graphql-go/graphql"
5 | "rxdrag.com/entify/authcontext"
6 | "rxdrag.com/entify/authentication"
7 | "rxdrag.com/entify/utils"
8 | )
9 |
10 | func Logout(p graphql.ResolveParams) (interface{}, error) {
11 | defer utils.PrintErrorStack()
12 | token := authcontext.ParseContextValues(p).Token
13 | if token != "" {
14 | authentication.Logout(token)
15 | }
16 | return true, nil
17 | }
18 |
--------------------------------------------------------------------------------
/resolve/me.go:
--------------------------------------------------------------------------------
1 | package resolve
2 |
3 | import (
4 | "github.com/graphql-go/graphql"
5 | "rxdrag.com/entify/authcontext"
6 | "rxdrag.com/entify/utils"
7 | )
8 |
9 | func Me(p graphql.ResolveParams) (interface{}, error) {
10 | defer utils.PrintErrorStack()
11 | return authcontext.ParseContextValues(p).Me, nil
12 | }
13 |
--------------------------------------------------------------------------------
/resolve/middleware.go:
--------------------------------------------------------------------------------
1 | package resolve
2 |
3 | import (
4 | "context"
5 | "net/http"
6 |
7 | "rxdrag.com/entify/consts"
8 | )
9 |
10 | func LoadersMiddleware(next http.Handler) http.Handler {
11 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
12 | ctx := context.WithValue(r.Context(), consts.LOADERS, CreateDataLoaders())
13 | next.ServeHTTP(w, r.WithContext(ctx))
14 | })
15 | }
16 |
--------------------------------------------------------------------------------
/resolve/mutation.go:
--------------------------------------------------------------------------------
1 | package resolve
2 |
3 | import (
4 | "github.com/graphql-go/graphql"
5 | "rxdrag.com/entify/consts"
6 | "rxdrag.com/entify/model/data"
7 | "rxdrag.com/entify/model/graph"
8 | "rxdrag.com/entify/repository"
9 | "rxdrag.com/entify/utils"
10 | )
11 |
12 | func PostOneResolveFn(entity *graph.Entity) graphql.FieldResolveFn {
13 | return func(p graphql.ResolveParams) (interface{}, error) {
14 | defer utils.PrintErrorStack()
15 | object := p.Args[consts.ARG_OBJECT].(map[string]interface{})
16 | ConvertId(object)
17 | v := makeEntityAbilityVerifier(p, entity.Uuid())
18 | instance := data.NewInstance(object, entity)
19 | return repository.SaveOne(instance, v)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/resolve/publish.go:
--------------------------------------------------------------------------------
1 | package resolve
2 |
3 | import (
4 | "fmt"
5 | "time"
6 |
7 | "github.com/graphql-go/graphql"
8 | "rxdrag.com/entify/consts"
9 | "rxdrag.com/entify/model"
10 | "rxdrag.com/entify/model/data"
11 | "rxdrag.com/entify/model/meta"
12 | "rxdrag.com/entify/repository"
13 | "rxdrag.com/entify/utils"
14 | )
15 |
16 | func doPublish(v *repository.AbilityVerifier) error {
17 | publishedMeta := repository.QueryPublishedMeta()
18 | nextMeta := repository.QueryNextMeta()
19 | fmt.Println("Start to publish")
20 | // fmt.Println("Published Meta ID:", publishedMeta.(utils.Object)["id"])
21 | // fmt.Println("Next Meta ID:", nextMeta.(utils.Object)["id"])
22 |
23 | if nextMeta == nil {
24 | panic("Can not find unpublished meta")
25 | }
26 | publishedModel := model.New(repository.DecodeContent(publishedMeta))
27 | nextModel := model.New(repository.DecodeContent(nextMeta))
28 | nextModel.Graph.Validate()
29 | diff := model.CreateDiff(publishedModel, nextModel)
30 | repository.ExcuteDiff(diff)
31 | fmt.Println("ExcuteDiff success")
32 | metaObj := nextMeta.(utils.Object)
33 | metaObj[consts.META_STATUS] = meta.META_STATUS_PUBLISHED
34 | metaObj[consts.META_PUBLISHEDAT] = time.Now()
35 | _, err := repository.SaveOne(data.NewInstance(metaObj, model.GlobalModel.Graph.GetMetaEntity()), v)
36 | if err != nil {
37 | return err
38 | }
39 | //repository.LoadModel()
40 |
41 | return nil
42 | }
43 |
44 | func PublishMetaResolve(p graphql.ResolveParams) (interface{}, error) {
45 | defer utils.PrintErrorStack()
46 | v := makeEntityAbilityVerifier(p, meta.META_ENTITY_UUID)
47 | doPublish(v)
48 | return "success", nil
49 | }
50 |
51 | func SyncMetaResolve(p graphql.ResolveParams) (interface{}, error) {
52 | object := p.Args[consts.ARG_OBJECT].(map[string]interface{})
53 | v := makeEntityAbilityVerifier(p, meta.META_ENTITY_UUID)
54 | return repository.InsertOne(data.NewInstance(object, model.GlobalModel.Graph.GetMetaEntity()), v)
55 | }
56 |
--------------------------------------------------------------------------------
/resolve/query.go:
--------------------------------------------------------------------------------
1 | package resolve
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/graphql-go/graphql"
7 | "rxdrag.com/entify/consts"
8 | "rxdrag.com/entify/model/graph"
9 | "rxdrag.com/entify/repository"
10 | "rxdrag.com/entify/utils"
11 | )
12 |
13 | func QueryOneInterfaceResolveFn(intf *graph.Interface) graphql.FieldResolveFn {
14 | return func(p graphql.ResolveParams) (interface{}, error) {
15 | defer utils.PrintErrorStack()
16 | v := makeInterfaceAbilityVerifier(p, intf)
17 | instance := repository.QueryOneInterface(intf, p.Args, v)
18 | return instance, nil
19 | }
20 | }
21 |
22 | func QueryInterfaceResolveFn(intf *graph.Interface) graphql.FieldResolveFn {
23 | return func(p graphql.ResolveParams) (interface{}, error) {
24 | defer utils.PrintErrorStack()
25 | v := makeInterfaceAbilityVerifier(p, intf)
26 | return repository.QueryInterface(intf, p.Args, v), nil
27 | }
28 | }
29 |
30 | func QueryOneEntityResolveFn(entity *graph.Entity) graphql.FieldResolveFn {
31 | return func(p graphql.ResolveParams) (interface{}, error) {
32 | defer utils.PrintErrorStack()
33 | v := makeEntityAbilityVerifier(p, entity.Uuid())
34 | instance := repository.QueryOneEntity(entity, p.Args, v)
35 | return instance, nil
36 | }
37 | }
38 |
39 | func QueryEntityResolveFn(entity *graph.Entity) graphql.FieldResolveFn {
40 | return func(p graphql.ResolveParams) (interface{}, error) {
41 | defer utils.PrintErrorStack()
42 | // for _, iSelection := range p.Info.Operation.GetSelectionSet().Selections {
43 | // switch selection := iSelection.(type) {
44 | // case *ast.Field:
45 | // //fmt.Println(selection.Directives[len(selection.Directives)-1].Name.Value)
46 | // case *ast.InlineFragment:
47 | // case *ast.FragmentSpread:
48 | // }
49 | // }
50 | v := makeEntityAbilityVerifier(p, entity.Uuid())
51 | return repository.QueryEntity(entity, p.Args, v), nil
52 | }
53 | }
54 |
55 | func QueryAssociationFn(asso *graph.Association) graphql.FieldResolveFn {
56 | return func(p graphql.ResolveParams) (interface{}, error) {
57 | var (
58 | source = p.Source.(map[string]interface{})
59 | v = p.Context.Value
60 | loaders = v(consts.LOADERS).(*Loaders)
61 | handleError = func(err error) error {
62 | return fmt.Errorf(err.Error())
63 | }
64 | )
65 | defer utils.PrintErrorStack()
66 |
67 | if loaders == nil {
68 | panic("Data loaders is nil")
69 | }
70 | loader := loaders.GetLoader(p, asso, p.Args)
71 | thunk := loader.Load(p.Context, NewKey(source[consts.ID].(uint64)))
72 | return func() (interface{}, error) {
73 | data, err := thunk()
74 | if err != nil {
75 | return nil, handleError(err)
76 | }
77 |
78 | var retValue interface{}
79 | if data == nil {
80 | if asso.IsArray() {
81 | retValue = []map[string]interface{}{}
82 | } else {
83 | retValue = nil
84 | }
85 | } else {
86 | retValue = data
87 | }
88 | return retValue, nil
89 | }, nil
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/resolve/verifier.go:
--------------------------------------------------------------------------------
1 | package resolve
2 |
3 | import (
4 | "github.com/graphql-go/graphql"
5 | "rxdrag.com/entify/model/graph"
6 | "rxdrag.com/entify/repository"
7 | )
8 |
9 | func makeEntityAbilityVerifier(p graphql.ResolveParams, entityUuid string) *repository.AbilityVerifier {
10 | verifier := repository.NewVerifier()
11 |
12 | verifier.Init(p, []string{entityUuid})
13 |
14 | // if !verifier.CanReadEntity() && !node.IsInterface() {
15 | // panic("No permission to read: " + node.Name())
16 | // }
17 |
18 | // args := verifier.WeaveAuthInArgs(inputArgs)
19 | return verifier
20 | }
21 |
22 | func makeInterfaceAbilityVerifier(p graphql.ResolveParams, intf *graph.Interface) *repository.AbilityVerifier {
23 | verifier := repository.NewVerifier()
24 | var uuids []string
25 | for i := range intf.Children {
26 | uuids = append(uuids, intf.Children[i].Uuid())
27 | }
28 | verifier.Init(p, uuids)
29 | return verifier
30 | }
31 |
32 | func makeAssociAbilityVerifier(p graphql.ResolveParams, association *graph.Association) *repository.AbilityVerifier {
33 | verifier := repository.NewVerifier()
34 | typeInterface := association.TypeInterface()
35 | if typeInterface != nil {
36 | var uuids []string
37 | for i := range typeInterface.Children {
38 | uuids = append(uuids, typeInterface.Children[i].Uuid())
39 | }
40 | verifier.Init(p, uuids)
41 | } else {
42 | verifier.Init(p, []string{association.TypeClass().Uuid()})
43 | }
44 | return verifier
45 | }
46 |
--------------------------------------------------------------------------------
/scalars/federation.go:
--------------------------------------------------------------------------------
1 | package scalars
2 |
3 | import (
4 | "github.com/graphql-go/graphql"
5 | "rxdrag.com/entify/consts"
6 | )
7 |
8 | var AnyType = graphql.NewScalar(
9 | graphql.ScalarConfig{
10 | Name: consts.SCALAR_ANY,
11 | Description: "The `_Any` scalar type represents _Any values as specified by [Federation](https://www.apollographql.com/docs/federation/federation-spec)",
12 | Serialize: func(value interface{}) interface{} {
13 | return value
14 | },
15 | ParseValue: func(value interface{}) interface{} {
16 | return value
17 | },
18 | ParseLiteral: parseLiteral,
19 | },
20 | )
21 |
22 | var FieldSetType = graphql.NewScalar(
23 | graphql.ScalarConfig{
24 | Name: consts.SCALAR_FIELDSET,
25 | Description: "The `_FieldSet` scalar type represents _FieldSet values as specified by [Federation](https://www.apollographql.com/docs/federation/federation-spec)",
26 | Serialize: func(value interface{}) interface{} {
27 | return value
28 | },
29 | ParseValue: func(value interface{}) interface{} {
30 | return value
31 | },
32 | ParseLiteral: parseLiteral,
33 | },
34 | )
35 |
--------------------------------------------------------------------------------
/scalars/json.go:
--------------------------------------------------------------------------------
1 | package scalars
2 |
3 | import (
4 | "github.com/graphql-go/graphql"
5 | "github.com/graphql-go/graphql/language/ast"
6 | "github.com/graphql-go/graphql/language/kinds"
7 | )
8 |
9 | func parseLiteral(astValue ast.Value) interface{} {
10 | kind := astValue.GetKind()
11 |
12 | switch kind {
13 | case kinds.StringValue:
14 | return astValue.GetValue()
15 | case kinds.BooleanValue:
16 | return astValue.GetValue()
17 | case kinds.IntValue:
18 | return astValue.GetValue()
19 | case kinds.FloatValue:
20 | return astValue.GetValue()
21 | case kinds.ObjectValue:
22 | obj := make(map[string]interface{})
23 | for _, v := range astValue.GetValue().([]*ast.ObjectField) {
24 | obj[v.Name.Value] = parseLiteral(v.Value)
25 | }
26 | return obj
27 | case kinds.ListValue:
28 | list := make([]interface{}, 0)
29 | for _, v := range astValue.GetValue().([]ast.Value) {
30 | list = append(list, parseLiteral(v))
31 | }
32 | return list
33 | default:
34 | return nil
35 | }
36 | }
37 |
38 | // JSON type
39 | var JSONType = graphql.NewScalar(
40 | graphql.ScalarConfig{
41 | Name: "JSON",
42 | Description: "The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf)",
43 | Serialize: func(value interface{}) interface{} {
44 | return value
45 | },
46 | ParseValue: func(value interface{}) interface{} {
47 | return value
48 | },
49 | ParseLiteral: parseLiteral,
50 | },
51 | )
52 |
--------------------------------------------------------------------------------
/scalars/upload.go:
--------------------------------------------------------------------------------
1 | package scalars
2 |
3 | import "github.com/graphql-go/graphql"
4 |
5 | // Upload type
6 | var UploadType = graphql.NewScalar(
7 | graphql.ScalarConfig{
8 | Name: "Upload",
9 | Description: "The `Upload` scalar type ",
10 | Serialize: func(value interface{}) interface{} {
11 | return value
12 | },
13 | ParseValue: func(value interface{}) interface{} {
14 | return value
15 | },
16 | ParseLiteral: parseLiteral,
17 | },
18 | )
19 |
--------------------------------------------------------------------------------
/schema/cache.go:
--------------------------------------------------------------------------------
1 | package schema
2 |
3 | import (
4 | "github.com/graphql-go/graphql"
5 | "rxdrag.com/entify/model"
6 | "rxdrag.com/entify/model/graph"
7 | )
8 |
9 | type TypeCache struct {
10 | ObjectTypeMap map[string]*graphql.Object
11 | ObjectMapById map[uint64]*graphql.Object
12 | EnumTypeMap map[string]*graphql.Enum
13 | InterfaceTypeMap map[string]*graphql.Interface
14 | SetInputMap map[string]*graphql.InputObject
15 | SaveInputMap map[string]*graphql.InputObject
16 | HasManyInputMap map[string]*graphql.InputObject
17 | HasOneInputMap map[string]*graphql.InputObject
18 | WhereExpMap map[string]*graphql.InputObject
19 | DistinctOnEnumMap map[string]*graphql.Enum
20 | OrderByMap map[string]*graphql.InputObject
21 | EnumComparisonExpMap map[string]*graphql.InputObjectFieldConfig
22 | MutationResponseMap map[string]*graphql.Object
23 | AggregateMap map[string]*graphql.Object
24 | SelectColumnsMap map[string]*graphql.InputObject
25 | }
26 |
27 | func (c *TypeCache) MakeCache() {
28 | c.clearCache()
29 | c.makeEnums(model.GlobalModel.Graph.Enums)
30 | c.makeOutputInterfaces(model.GlobalModel.Graph.Interfaces)
31 | c.makeEntityOutputObjects(model.GlobalModel.Graph.Entities)
32 | c.makePartialOutputObjects(model.GlobalModel.Graph.Partials)
33 | c.makeExternalOutputObjects(model.GlobalModel.Graph.Externals)
34 | c.makeQueryArgs()
35 | c.makeRelations()
36 | c.makeInputs()
37 | }
38 |
39 | // func (c *TypeCache) OutputInterfaceType(entity *model.Entity) graphql.Type {
40 | // return c.InterfaceTypeMap[entity.Name]
41 | // }
42 |
43 | func (c *TypeCache) InterfaceOutputType(name string) *graphql.Interface {
44 | intf := c.InterfaceTypeMap[name]
45 | if intf != nil {
46 | return intf
47 | }
48 | panic("Can not find interface output type of " + name)
49 | }
50 |
51 | func (c *TypeCache) EntityeOutputType(name string) *graphql.Object {
52 | obj := c.ObjectTypeMap[name]
53 | if obj == nil {
54 | panic("Can not find output type of " + name)
55 | }
56 | return obj
57 | }
58 |
59 | func (c *TypeCache) OutputType(name string) graphql.Type {
60 | intf := c.InterfaceTypeMap[name]
61 | if intf != nil {
62 | return intf
63 | }
64 | obj := c.ObjectTypeMap[name]
65 | if obj == nil {
66 | panic("Can not find output type of " + name)
67 | }
68 | return obj
69 | }
70 |
71 | func (c *TypeCache) GetEntityTypeByInnerId(id uint64) *graphql.Object {
72 | return c.ObjectMapById[id]
73 | }
74 |
75 | func (c *TypeCache) EntityTypes() []graphql.Type {
76 | objs := []graphql.Type{}
77 | for key := range c.ObjectTypeMap {
78 | objs = append(objs, c.ObjectTypeMap[key])
79 | }
80 |
81 | return objs
82 | }
83 |
84 | func (c *TypeCache) EntityObjects() []*graphql.Object {
85 | objs := []*graphql.Object{}
86 | for key := range c.ObjectTypeMap {
87 | objs = append(objs, c.ObjectTypeMap[key])
88 | }
89 |
90 | return objs
91 | }
92 |
93 | func (c *TypeCache) EnumType(name string) *graphql.Enum {
94 | return c.EnumTypeMap[name]
95 | }
96 |
97 | func (c *TypeCache) WhereExp(name string) *graphql.InputObject {
98 | return c.WhereExpMap[name]
99 | }
100 |
101 | func (c *TypeCache) OrderByExp(name string) *graphql.InputObject {
102 | return c.OrderByMap[name]
103 | }
104 |
105 | func (c *TypeCache) DistinctOnEnum(name string) *graphql.Enum {
106 | return c.DistinctOnEnumMap[name]
107 | }
108 |
109 | func (c *TypeCache) DistinctOnEnums() map[string]*graphql.Enum {
110 | return c.DistinctOnEnumMap
111 | }
112 |
113 | func (c *TypeCache) SaveInput(name string) *graphql.InputObject {
114 | return c.SaveInputMap[name]
115 | }
116 |
117 | func (c *TypeCache) SetInput(name string) *graphql.InputObject {
118 | return c.SetInputMap[name]
119 | }
120 | func (c *TypeCache) HasManyInput(name string) *graphql.InputObject {
121 | return c.HasManyInputMap[name]
122 | }
123 | func (c *TypeCache) HasOneInput(name string) *graphql.InputObject {
124 | return c.HasOneInputMap[name]
125 | }
126 |
127 | func (c *TypeCache) MutationResponse(name string) *graphql.Object {
128 | return c.MutationResponseMap[name]
129 | }
130 |
131 | func (c *TypeCache) mapInterfaces(entities []*graph.Interface) []*graphql.Interface {
132 | interfaces := []*graphql.Interface{}
133 | for i := range entities {
134 | interfaces = append(interfaces, c.InterfaceTypeMap[entities[i].Domain.Name])
135 | }
136 |
137 | return interfaces
138 | }
139 |
140 | func (c *TypeCache) clearCache() {
141 | c.ObjectTypeMap = make(map[string]*graphql.Object)
142 | c.ObjectMapById = make(map[uint64]*graphql.Object)
143 | c.EnumTypeMap = make(map[string]*graphql.Enum)
144 | c.InterfaceTypeMap = make(map[string]*graphql.Interface)
145 | c.SetInputMap = make(map[string]*graphql.InputObject)
146 | c.SaveInputMap = make(map[string]*graphql.InputObject)
147 | c.HasManyInputMap = make(map[string]*graphql.InputObject)
148 | c.HasOneInputMap = make(map[string]*graphql.InputObject)
149 | c.WhereExpMap = make(map[string]*graphql.InputObject)
150 | c.DistinctOnEnumMap = make(map[string]*graphql.Enum)
151 | c.OrderByMap = make(map[string]*graphql.InputObject)
152 | c.EnumComparisonExpMap = make(map[string]*graphql.InputObjectFieldConfig)
153 | c.MutationResponseMap = make(map[string]*graphql.Object)
154 | c.AggregateMap = make(map[string]*graphql.Object)
155 | c.SelectColumnsMap = make(map[string]*graphql.InputObject)
156 | }
157 |
--------------------------------------------------------------------------------
/schema/enum.go:
--------------------------------------------------------------------------------
1 | package schema
2 |
3 | import (
4 | "github.com/graphql-go/graphql"
5 | "rxdrag.com/entify/model/graph"
6 | )
7 |
8 | func (c *TypeCache) makeEnums(enums []*graph.Enum) {
9 | for i := range enums {
10 | enum := enums[i]
11 | c.EnumTypeMap[enum.Name] = EnumType(enum)
12 | }
13 | }
14 |
15 | func EnumType(entity *graph.Enum) *graphql.Enum {
16 | enumValueConfigMap := graphql.EnumValueConfigMap{}
17 | for _, value := range entity.Values {
18 | enumValueConfigMap[value] = &graphql.EnumValueConfig{
19 | Value: value,
20 | }
21 | }
22 | enum := graphql.NewEnum(
23 | graphql.EnumConfig{
24 | Name: entity.Name,
25 | Values: enumValueConfigMap,
26 | },
27 | )
28 | return enum
29 | }
30 |
--------------------------------------------------------------------------------
/schema/federation.go:
--------------------------------------------------------------------------------
1 | package schema
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | var allSDL = `
8 | extend schema
9 | @link(url: "https://specs.apollo.dev/federation/v2.0",
10 | import: ["@key"])
11 |
12 | scalar JSON
13 | scalar DateTime
14 | scalar Upload
15 |
16 | type Query {
17 | %s
18 | }
19 |
20 | type Mutation {
21 | %s
22 | }
23 | %s
24 | `
25 |
26 | func makeFederationSDL() string {
27 | sdl := allSDL
28 | queryFields, queryTypes := querySDL()
29 | mutationFields, mutationTypes := mutationSDL()
30 | return fmt.Sprintf(sdl, queryFields, mutationFields, queryTypes+mutationTypes)
31 | }
32 |
--------------------------------------------------------------------------------
/schema/federationmutation.go:
--------------------------------------------------------------------------------
1 | package schema
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/graphql-go/graphql"
7 | "rxdrag.com/entify/config"
8 | "rxdrag.com/entify/consts"
9 | "rxdrag.com/entify/model"
10 | "rxdrag.com/entify/model/graph"
11 | "rxdrag.com/entify/model/meta"
12 | )
13 |
14 | var mutationFieldSDL = "\t%s(%s) : %s \n"
15 |
16 | func mutationSDL() (string, string) {
17 | queryFields := ""
18 | types := ""
19 | if config.AuthUrl() == "" {
20 | queryFields = queryFields + makeLoginSDL()
21 | }
22 |
23 | for _, entity := range model.GlobalModel.Graph.RootEnities() {
24 | if notSystemEntity(entity) {
25 | queryFields = queryFields + makeEntityMutationSDL(entity)
26 | types = types + objectToSDL(Cache.MutationResponse(entity.Name()), false)
27 | }
28 | }
29 | for _, partial := range model.GlobalModel.Graph.RootPartails() {
30 | queryFields = queryFields + makePartialMutationSDL(partial)
31 | types = types + objectToSDL(Cache.MutationResponse(partial.Name()), false)
32 | }
33 |
34 | // for _, exteneral := range model.GlobalModel.Graph.RootExternals() {
35 | // queryFields = queryFields + makeExteneralSDL(exteneral)
36 | // //types = types + objectToSDL(Cache.EntityeOutputType(exteneral.Name()))
37 | // }
38 | for _, input := range Cache.SetInputMap {
39 | if len(input.Fields()) > 0 &&
40 | input.Name() != meta.MetaClass.Name &&
41 | input.Name() != meta.MetaClass.Name+consts.SET &&
42 | input.Name() != meta.AbilityClass.Name &&
43 | input.Name() != meta.EntityAuthSettingsClass.Name {
44 | types = types + inputToSDL(input)
45 | }
46 |
47 | }
48 | for _, input := range Cache.SaveInputMap {
49 | types = types + inputToSDL(input)
50 | }
51 | for _, input := range Cache.HasManyInputMap {
52 | types = types + inputToSDL(input)
53 | }
54 | for _, input := range Cache.HasOneInputMap {
55 | types = types + inputToSDL(input)
56 | }
57 |
58 | return queryFields, types
59 | }
60 |
61 | func makeEntityMutationSDL(entity *graph.Entity) string {
62 | sdl := ""
63 | sdl = sdl + fmt.Sprintf(mutationFieldSDL,
64 | entity.DeleteName(),
65 | makeArgsSDL(deleteArgs(entity)),
66 | Cache.MutationResponse(entity.Name()).Name(),
67 | )
68 |
69 | sdl = sdl + fmt.Sprintf(mutationFieldSDL,
70 | entity.DeleteByIdName(),
71 | makeArgsSDL(deleteByIdArgs()),
72 | Cache.OutputType(entity.Name()).String(),
73 | )
74 |
75 | updateInput := Cache.SetInput(entity.Name())
76 | if len(updateInput.Fields()) > 0 {
77 | sdl = sdl + fmt.Sprintf(mutationFieldSDL,
78 | entity.SetName(),
79 | makeArgsSDL(setArgs(entity)),
80 | Cache.MutationResponse(entity.Name()).String(),
81 | )
82 | }
83 |
84 | sdl = sdl + fmt.Sprintf(mutationFieldSDL,
85 | entity.UpsertName(),
86 | makeArgsSDL(upsertArgs(entity)),
87 | (&graphql.List{OfType: Cache.OutputType(entity.Name())}).String(),
88 | )
89 |
90 | sdl = sdl + fmt.Sprintf(mutationFieldSDL,
91 | entity.UpsertOneName(),
92 | makeArgsSDL(upsertOneArgs(entity)),
93 | Cache.OutputType(entity.Name()).String(),
94 | )
95 |
96 | return sdl
97 | }
98 |
99 | func makePartialMutationSDL(partial *graph.Partial) string {
100 | sdl := ""
101 | sdl = sdl + fmt.Sprintf(mutationFieldSDL,
102 | partial.DeleteName(),
103 | makeArgsSDL(deleteArgs(&partial.Entity)),
104 | Cache.MutationResponse(partial.Name()).Name(),
105 | )
106 |
107 | sdl = sdl + fmt.Sprintf(mutationFieldSDL,
108 | partial.DeleteByIdName(),
109 | makeArgsSDL(deleteByIdArgs()),
110 | Cache.OutputType(partial.Name()).String(),
111 | )
112 |
113 | sdl = sdl + fmt.Sprintf(mutationFieldSDL,
114 | partial.InsertName(),
115 | makeArgsSDL(upsertArgs(&partial.Entity)),
116 | (&graphql.List{OfType: Cache.OutputType(partial.Name())}).String(),
117 | )
118 |
119 | sdl = sdl + fmt.Sprintf(mutationFieldSDL,
120 | partial.InsertOneName(),
121 | makeArgsSDL(upsertOneArgs(&partial.Entity)),
122 | Cache.OutputType(partial.Name()).String(),
123 | )
124 |
125 | updateInput := Cache.SetInput(partial.Name())
126 | if len(updateInput.Fields()) > 0 {
127 | sdl = sdl + fmt.Sprintf(mutationFieldSDL,
128 | partial.SetName(),
129 | makeArgsSDL(setArgs(&partial.Entity)),
130 | Cache.MutationResponse(partial.Name()).String(),
131 | )
132 | }
133 |
134 | sdl = sdl + fmt.Sprintf(mutationFieldSDL,
135 | partial.UpdateName(),
136 | makeArgsSDL(upsertArgs(&partial.Entity)),
137 | (&graphql.List{OfType: Cache.OutputType(partial.Name())}).String(),
138 | )
139 |
140 | sdl = sdl + fmt.Sprintf(mutationFieldSDL,
141 | partial.UpdateOneName(),
142 | makeArgsSDL(upsertOneArgs(&partial.Entity)),
143 | Cache.OutputType(partial.Name()).String(),
144 | )
145 |
146 | return sdl
147 | }
148 |
149 | func makeLoginSDL() string {
150 | return ` login(loginName: String!password: String!): String
151 | logout: Boolean`
152 | }
153 |
--------------------------------------------------------------------------------
/schema/globals.go:
--------------------------------------------------------------------------------
1 | package schema
2 |
3 | import (
4 | "github.com/graphql-go/graphql"
5 | "rxdrag.com/entify/config"
6 | "rxdrag.com/entify/consts"
7 | "rxdrag.com/entify/resolve"
8 | "rxdrag.com/entify/utils"
9 | )
10 |
11 | var Cache TypeCache
12 |
13 | var _ServiceType = graphql.NewObject(
14 | graphql.ObjectConfig{
15 | Name: consts.SERVICE_TYPE,
16 | Fields: graphql.Fields{
17 | consts.ID: &graphql.Field{ //扩展一个id字段
18 | Type: graphql.Int,
19 | Resolve: func(p graphql.ResolveParams) (interface{}, error) {
20 | defer utils.PrintErrorStack()
21 | return config.ServiceId(), nil
22 | },
23 | },
24 |
25 | //扩展字段
26 | consts.INSTALLED: &graphql.Field{
27 | Type: graphql.Boolean,
28 | Resolve: func(p graphql.ResolveParams) (interface{}, error) {
29 | defer utils.PrintErrorStack()
30 | return true, nil
31 | },
32 | },
33 |
34 | consts.CAN_UPLOAD: &graphql.Field{
35 | Type: graphql.Boolean,
36 | Resolve: func(p graphql.ResolveParams) (interface{}, error) {
37 | defer utils.PrintErrorStack()
38 | return config.Storage() != "", nil
39 | },
40 | },
41 |
42 | consts.SDL: &graphql.Field{
43 | Type: graphql.String,
44 | Description: "Service SDL",
45 | Resolve: func(p graphql.ResolveParams) (interface{}, error) {
46 | defer utils.PrintErrorStack()
47 | return makeFederationSDL(), nil
48 | },
49 | },
50 | },
51 | Description: "_Service type of federation schema specification, and extends other fields",
52 | },
53 | )
54 |
55 | var EntityType *graphql.Union
56 |
57 | var installInputType = graphql.NewInputObject(
58 | graphql.InputObjectConfig{
59 | Name: "InstallInput",
60 | Fields: graphql.InputObjectConfigFieldMap{
61 | consts.ADMIN: &graphql.InputObjectFieldConfig{
62 | Type: &graphql.NonNull{
63 | OfType: graphql.String,
64 | },
65 | },
66 | consts.ADMINPASSWORD: &graphql.InputObjectFieldConfig{
67 | Type: &graphql.NonNull{
68 | OfType: graphql.String,
69 | },
70 | },
71 | consts.WITHDEMO: &graphql.InputObjectFieldConfig{
72 | Type: graphql.Boolean,
73 | },
74 | },
75 | },
76 | )
77 |
78 | var fileOutputType = graphql.NewObject(
79 | graphql.ObjectConfig{
80 | Name: consts.FILE,
81 | Fields: graphql.Fields{
82 | consts.FILE_NAME: &graphql.Field{
83 | Type: graphql.String,
84 | },
85 | consts.FILE_SIZE: &graphql.Field{
86 | Type: graphql.Int,
87 | },
88 | consts.FILE_MIMETYPE: &graphql.Field{
89 | Type: graphql.String,
90 | },
91 | consts.FILE_URL: &graphql.Field{
92 | Type: graphql.String,
93 | Resolve: resolve.FileUrlResolve,
94 | },
95 | consts.File_EXTNAME: &graphql.Field{
96 | Type: graphql.String,
97 | },
98 | consts.FILE_THMUBNAIL: &graphql.Field{
99 | Type: graphql.String,
100 | },
101 | consts.FILE_RESIZE: &graphql.Field{
102 | Type: graphql.String,
103 | Args: graphql.FieldConfigArgument{
104 | consts.FILE_WIDTH: &graphql.ArgumentConfig{
105 | Type: graphql.Int,
106 | },
107 | consts.FILE_HEIGHT: &graphql.ArgumentConfig{
108 | Type: graphql.Int,
109 | },
110 | },
111 | },
112 | },
113 | Description: "File type",
114 | },
115 | )
116 |
117 | var baseRoleTye = graphql.NewObject(
118 | graphql.ObjectConfig{
119 | Name: "BaseRole",
120 | Fields: graphql.Fields{
121 | consts.ID: &graphql.Field{
122 | Type: graphql.ID,
123 | },
124 | consts.NAME: &graphql.Field{
125 | Type: graphql.String,
126 | },
127 | },
128 | Description: "Base role for auth",
129 | },
130 | )
131 |
132 | var baseUserType = graphql.NewObject(
133 | graphql.ObjectConfig{
134 | Name: "BaseUser",
135 | Fields: graphql.Fields{
136 | consts.ID: &graphql.Field{
137 | Type: graphql.ID,
138 | },
139 | consts.NAME: &graphql.Field{
140 | Type: graphql.String,
141 | },
142 | consts.LOGIN_NAME: &graphql.Field{
143 | Type: graphql.String,
144 | },
145 | consts.IS_SUPPER: &graphql.Field{
146 | Type: graphql.Boolean,
147 | },
148 | consts.IS_DEMO: &graphql.Field{
149 | Type: graphql.Boolean,
150 | },
151 | "roles": &graphql.Field{
152 | Type: &graphql.List{
153 | OfType: baseRoleTye,
154 | },
155 | },
156 | },
157 | Description: "Base user for auth",
158 | },
159 | )
160 |
--------------------------------------------------------------------------------
/schema/interface.go:
--------------------------------------------------------------------------------
1 | package schema
2 |
3 | import (
4 | "github.com/graphql-go/graphql"
5 | "rxdrag.com/entify/consts"
6 | "rxdrag.com/entify/model/graph"
7 | "rxdrag.com/entify/utils"
8 | )
9 |
10 | func (c *TypeCache) makeOutputInterfaces(interfaces []*graph.Interface) {
11 | for i := range interfaces {
12 | intf := interfaces[i]
13 | c.InterfaceTypeMap[intf.Name()] = c.InterfaceType(intf)
14 | }
15 | }
16 |
17 | func (c *TypeCache) InterfaceType(intf *graph.Interface) *graphql.Interface {
18 | name := intf.Name()
19 |
20 | return graphql.NewInterface(
21 | graphql.InterfaceConfig{
22 | Name: name,
23 | Fields: outputFields(intf.AllAttributes(), intf.AllMethods()),
24 | Description: intf.Description(),
25 | ResolveType: resolveTypeFn,
26 | },
27 | )
28 | }
29 |
30 | func resolveTypeFn(p graphql.ResolveTypeParams) *graphql.Object {
31 | if value, ok := p.Value.(map[string]interface{}); ok {
32 | if id, ok := value[consts.ID].(uint64); ok {
33 | entityInnerId := utils.DecodeEntityInnerId(id)
34 | return Cache.GetEntityTypeByInnerId(entityInnerId)
35 | }
36 | }
37 | return nil
38 | }
39 |
--------------------------------------------------------------------------------
/schema/model.go:
--------------------------------------------------------------------------------
1 | package schema
2 |
3 | import (
4 | "github.com/mitchellh/mapstructure"
5 | "rxdrag.com/entify/consts"
6 | "rxdrag.com/entify/model"
7 | "rxdrag.com/entify/model/graph"
8 | "rxdrag.com/entify/model/meta"
9 |
10 | "rxdrag.com/entify/repository"
11 | "rxdrag.com/entify/utils"
12 | )
13 |
14 | func QueryPublishedMeta() interface{} {
15 | publishedMeta := repository.QueryOneEntity(model.GlobalModel.Graph.GetMetaEntity(), graph.QueryArg{
16 | consts.ARG_WHERE: graph.QueryArg{
17 | consts.META_STATUS: graph.QueryArg{
18 | consts.ARG_EQ: meta.META_STATUS_PUBLISHED,
19 | },
20 | },
21 | }, repository.NewSupperVerifier())
22 |
23 | return publishedMeta
24 | }
25 |
26 | func QueryNextMeta() interface{} {
27 | nextMeta := repository.QueryOneEntity(model.GlobalModel.Graph.GetMetaEntity(), graph.QueryArg{
28 | consts.ARG_WHERE: graph.QueryArg{
29 | consts.META_STATUS: graph.QueryArg{
30 | consts.ARG_ISNULL: true,
31 | },
32 | },
33 | }, repository.NewSupperVerifier())
34 |
35 | return nextMeta
36 | }
37 |
38 | func DecodeContent(obj interface{}) *meta.MetaContent {
39 | content := meta.MetaContent{}
40 | if obj != nil {
41 | err := mapstructure.Decode(obj.(utils.Object)[consts.META_CONTENT], &content)
42 | if err != nil {
43 | panic("Decode content failure:" + err.Error())
44 | }
45 | }
46 | return &content
47 | }
48 |
49 | func LoadModel() {
50 | //初始值,用户取meta信息,取完后,换掉该部分内容
51 | initMeta := meta.MetaContent{
52 | Classes: []meta.ClassMeta{
53 | meta.MetaStatusEnum,
54 | meta.MetaClass,
55 | meta.EntityAuthSettingsClass,
56 | meta.AbilityTypeEnum,
57 | meta.AbilityClass,
58 | },
59 | }
60 | model.GlobalModel = model.New(&initMeta)
61 | publishedMeta := QueryPublishedMeta()
62 | publishedContent := DecodeContent(publishedMeta)
63 | publishedContent.Classes = append(publishedContent.Classes,
64 | meta.MetaStatusEnum,
65 | meta.MetaClass,
66 | meta.EntityAuthSettingsClass,
67 | meta.AbilityTypeEnum,
68 | meta.AbilityClass,
69 | )
70 |
71 | model.GlobalModel = model.New(publishedContent)
72 | }
73 |
74 | // func init() {
75 | // LoadModel()
76 | // }
77 |
--------------------------------------------------------------------------------
/schema/mutation.go:
--------------------------------------------------------------------------------
1 | package schema
2 |
3 | import (
4 | "github.com/graphql-go/graphql"
5 | "rxdrag.com/entify/authentication"
6 | "rxdrag.com/entify/config"
7 | "rxdrag.com/entify/consts"
8 | "rxdrag.com/entify/model"
9 | "rxdrag.com/entify/model/graph"
10 | "rxdrag.com/entify/resolve"
11 | "rxdrag.com/entify/utils"
12 | )
13 |
14 | const INPUT = "input"
15 |
16 | func appendAuthMutation(fields graphql.Fields) {
17 | fields[consts.LOGIN] = &graphql.Field{
18 | Type: graphql.String,
19 | Args: graphql.FieldConfigArgument{
20 | consts.LOGIN_NAME: &graphql.ArgumentConfig{
21 | Type: &graphql.NonNull{OfType: graphql.String},
22 | },
23 | consts.PASSWORD: &graphql.ArgumentConfig{
24 | Type: &graphql.NonNull{OfType: graphql.String},
25 | },
26 | },
27 | Resolve: func(p graphql.ResolveParams) (interface{}, error) {
28 | defer utils.PrintErrorStack()
29 | return authentication.Login(p.Args[consts.LOGIN_NAME].(string), p.Args[consts.PASSWORD].(string))
30 | },
31 | }
32 |
33 | fields[consts.LOGOUT] = &graphql.Field{
34 | Type: graphql.Boolean,
35 | Resolve: resolve.Logout,
36 | }
37 | }
38 |
39 | func rootMutation() *graphql.Object {
40 | metaEntity := model.GlobalModel.Graph.GetMetaEntity()
41 | mutationFields := graphql.Fields{
42 | consts.PUBLISH: &graphql.Field{
43 | Type: Cache.OutputType(metaEntity.Name()),
44 | Resolve: publishResolve,
45 | },
46 | consts.ROLLBACK: &graphql.Field{
47 | Type: Cache.OutputType(metaEntity.Name()),
48 | Resolve: resolve.SyncMetaResolve,
49 | },
50 | consts.SYNC_META: &graphql.Field{
51 | Type: Cache.OutputType(metaEntity.Name()),
52 | Resolve: resolve.SyncMetaResolve,
53 | },
54 | }
55 |
56 | for _, entity := range model.GlobalModel.Graph.RootEnities() {
57 | if entity.Domain.Root {
58 | appendEntityMutationToFields(entity, mutationFields)
59 | }
60 | }
61 |
62 | for _, partial := range model.GlobalModel.Graph.RootPartails() {
63 | if partial.Domain.Root {
64 | appendPartialMutationToFields(partial, mutationFields)
65 | }
66 | }
67 |
68 | if config.AuthUrl() == "" {
69 | appendAuthMutation(mutationFields)
70 | }
71 |
72 | rootMutation := graphql.ObjectConfig{
73 | Name: consts.ROOT_MUTATION_NAME,
74 | Fields: mutationFields,
75 | Description: "Root mutation of entity engine.",
76 | }
77 |
78 | return graphql.NewObject(rootMutation)
79 | }
80 |
81 | func deleteArgs(entity *graph.Entity) graphql.FieldConfigArgument {
82 | return graphql.FieldConfigArgument{
83 | consts.ARG_WHERE: &graphql.ArgumentConfig{
84 | Type: Cache.WhereExp(entity.Name()),
85 | },
86 | }
87 | }
88 |
89 | func deleteByIdArgs() graphql.FieldConfigArgument {
90 | return graphql.FieldConfigArgument{
91 | consts.ID: &graphql.ArgumentConfig{
92 | Type: graphql.Int,
93 | },
94 | }
95 | }
96 |
97 | func upsertArgs(entity *graph.Entity) graphql.FieldConfigArgument {
98 | return graphql.FieldConfigArgument{
99 | consts.ARG_OBJECTS: &graphql.ArgumentConfig{
100 | Type: &graphql.NonNull{
101 | OfType: &graphql.List{
102 | OfType: &graphql.NonNull{
103 | OfType: Cache.SaveInput(entity.Name()),
104 | },
105 | },
106 | },
107 | },
108 | }
109 | }
110 |
111 | func upsertOneArgs(entity *graph.Entity) graphql.FieldConfigArgument {
112 | return graphql.FieldConfigArgument{
113 | consts.ARG_OBJECT: &graphql.ArgumentConfig{
114 | Type: &graphql.NonNull{
115 | OfType: Cache.SaveInput(entity.Name()),
116 | },
117 | },
118 | }
119 | }
120 |
121 | func setArgs(entity *graph.Entity) graphql.FieldConfigArgument {
122 | updateInput := Cache.SetInput(entity.Name())
123 | return graphql.FieldConfigArgument{
124 | consts.ARG_SET: &graphql.ArgumentConfig{
125 | Type: &graphql.NonNull{
126 | OfType: updateInput,
127 | },
128 | },
129 | consts.ARG_WHERE: &graphql.ArgumentConfig{
130 | Type: Cache.WhereExp(entity.Name()),
131 | },
132 | }
133 | }
134 |
135 | func appendEntityMutationToFields(entity *graph.Entity, feilds graphql.Fields) {
136 | (feilds)[entity.DeleteName()] = &graphql.Field{
137 | Type: Cache.MutationResponse(entity.Name()),
138 | Args: deleteArgs(entity),
139 | //Resolve: entity.QueryResolve(),
140 | }
141 | (feilds)[entity.DeleteByIdName()] = &graphql.Field{
142 | Type: Cache.OutputType(entity.Name()),
143 | Args: deleteByIdArgs(),
144 | //Resolve: entity.QueryResolve(),
145 | }
146 | (feilds)[entity.UpsertName()] = &graphql.Field{
147 | Type: &graphql.List{OfType: Cache.OutputType(entity.Name())},
148 | Args: upsertArgs(entity),
149 | }
150 | //Resolve: entity.QueryResolve(),
151 | (feilds)[entity.UpsertOneName()] = &graphql.Field{
152 | Type: Cache.OutputType(entity.Name()),
153 | Args: upsertOneArgs(entity),
154 | Resolve: resolve.PostOneResolveFn(entity),
155 | }
156 |
157 | updateInput := Cache.SetInput(entity.Name())
158 | if len(updateInput.Fields()) > 0 {
159 | (feilds)[entity.SetName()] = &graphql.Field{
160 | Type: Cache.MutationResponse(entity.Name()),
161 | Args: setArgs(entity),
162 | //Resolve: entity.QueryResolve(),
163 | }
164 | }
165 |
166 | }
167 |
168 | func appendPartialMutationToFields(partial *graph.Partial, feilds graphql.Fields) {
169 |
170 | (feilds)[partial.DeleteName()] = &graphql.Field{
171 | Type: Cache.MutationResponse(partial.Name()),
172 | Args: deleteArgs(&partial.Entity),
173 | //Resolve: entity.QueryResolve(),
174 | }
175 | (feilds)[partial.DeleteByIdName()] = &graphql.Field{
176 | Type: Cache.OutputType(partial.Name()),
177 | Args: deleteByIdArgs(),
178 | //Resolve: entity.QueryResolve(),
179 | }
180 | (feilds)[partial.InsertName()] = &graphql.Field{
181 | Type: &graphql.List{OfType: Cache.OutputType(partial.Name())},
182 | Args: upsertArgs(&partial.Entity),
183 | }
184 | //Resolve: entity.QueryResolve(),
185 | (feilds)[partial.InsertOneName()] = &graphql.Field{
186 | Type: Cache.OutputType(partial.Name()),
187 | Args: upsertOneArgs(&partial.Entity),
188 | //Resolve: resolve.PostOneResolveFn(&partial.Entity),
189 | }
190 | (feilds)[partial.UpdateName()] = &graphql.Field{
191 | Type: &graphql.List{OfType: Cache.OutputType(partial.Name())},
192 | Args: upsertArgs(&partial.Entity),
193 | }
194 | //Resolve: entity.QueryResolve(),
195 | (feilds)[partial.UpdateOneName()] = &graphql.Field{
196 | Type: Cache.OutputType(partial.Name()),
197 | Args: upsertOneArgs(&partial.Entity),
198 | //Resolve: resolve.PostOneResolveFn(&partial.Entity),
199 | }
200 |
201 | updateInput := Cache.SetInput(partial.Name())
202 | if len(updateInput.Fields()) > 0 {
203 | (feilds)[partial.SetName()] = &graphql.Field{
204 | Type: Cache.MutationResponse(partial.Name()),
205 | Args: setArgs(&partial.Entity),
206 | //Resolve: entity.QueryResolve(),
207 | }
208 | }
209 |
210 | }
211 |
--------------------------------------------------------------------------------
/schema/object.go:
--------------------------------------------------------------------------------
1 | package schema
2 |
3 | import (
4 | "github.com/graphql-go/graphql"
5 | "rxdrag.com/entify/model/graph"
6 | )
7 |
8 | func (c *TypeCache) makeEntityOutputObjects(entities []*graph.Entity) {
9 | for i := range entities {
10 | c.makeEntityObject(entities[i])
11 |
12 | }
13 | }
14 |
15 | func (c *TypeCache) makePartialOutputObjects(partials []*graph.Partial) {
16 | for i := range partials {
17 | patial := partials[i]
18 | c.makeEntityObject(&patial.Entity)
19 | // partialName := patial.NameWithPartial()
20 |
21 | // objType := graphql.NewObject(
22 | // graphql.ObjectConfig{
23 | // Name: partialName,
24 | // Fields: outputFields(patial.AllAttributes(), patial.AllMethods()),
25 | // Description: patial.Description(),
26 | // },
27 | // )
28 | // c.ObjectTypeMap[partialName] = objType
29 | }
30 | }
31 |
32 | func (c *TypeCache) makeExternalOutputObjects(externals []*graph.External) {
33 | for i := range externals {
34 | external := externals[i]
35 | c.makeEntityObject(&external.Entity)
36 | }
37 | }
38 |
39 | func (c *TypeCache) makeEntityObject(entity *graph.Entity) {
40 | objType := c.ObjectType(entity)
41 | c.ObjectTypeMap[entity.Name()] = objType
42 | c.ObjectMapById[entity.InnerId()] = objType
43 | }
44 |
45 | func (c *TypeCache) ObjectType(entity *graph.Entity) *graphql.Object {
46 | name := entity.Name()
47 | interfaces := c.mapInterfaces(entity.Interfaces)
48 |
49 | if len(interfaces) > 0 {
50 | return graphql.NewObject(
51 | graphql.ObjectConfig{
52 | Name: name,
53 | Fields: outputFields(entity.AllAttributes(), entity.AllMethods()),
54 | Description: entity.Description(),
55 | Interfaces: interfaces,
56 | },
57 | )
58 | } else {
59 | return graphql.NewObject(
60 | graphql.ObjectConfig{
61 | Name: name,
62 | Fields: outputFields(entity.AllAttributes(), entity.AllMethods()),
63 | Description: entity.Description(),
64 | },
65 | )
66 | }
67 |
68 | }
69 |
70 | func outputFields(attrs []*graph.Attribute, methods []*graph.Method) graphql.Fields {
71 | fields := graphql.Fields{}
72 | for _, attr := range attrs {
73 | fields[attr.Name] = &graphql.Field{
74 | Type: PropertyType(attr),
75 | Description: attr.Description,
76 | // Resolve: func(p graphql.ResolveParams) (interface{}, error) {
77 | // fmt.Println(p.Context.Value("data"))
78 | // return "world", nil
79 | // },
80 | }
81 | }
82 |
83 | for _, method := range methods {
84 | fields[method.GetName()] = &graphql.Field{
85 | Type: PropertyType(method),
86 | Description: method.Method.Description,
87 | // Resolve: func(p graphql.ResolveParams) (interface{}, error) {
88 | // fmt.Println(p.Context.Value("data"))
89 | // return "world", nil
90 | // },
91 | }
92 | }
93 | return fields
94 | }
95 |
--------------------------------------------------------------------------------
/schema/orderby.go:
--------------------------------------------------------------------------------
1 | package schema
2 |
3 | import "github.com/graphql-go/graphql"
4 |
5 | var EnumOrderBy = graphql.NewEnum(
6 | graphql.EnumConfig{
7 | Name: "OrderBy",
8 | Values: graphql.EnumValueConfigMap{
9 | "asc": &graphql.EnumValueConfig{
10 | Value: "asc",
11 | },
12 | "ascNullsFirst": &graphql.EnumValueConfig{
13 | Value: "ascNullsFirst",
14 | },
15 | "ascNullsLast": &graphql.EnumValueConfig{
16 | Value: "ascNullsLast",
17 | },
18 | "desc": &graphql.EnumValueConfig{
19 | Value: "desc",
20 | },
21 | "descNullsFirst": &graphql.EnumValueConfig{
22 | Value: "descNullsFirst",
23 | },
24 | "descNullsLast": &graphql.EnumValueConfig{
25 | Value: "descNullsLast",
26 | },
27 | },
28 | },
29 | )
30 |
--------------------------------------------------------------------------------
/schema/property.go:
--------------------------------------------------------------------------------
1 | package schema
2 |
3 | import (
4 | "github.com/graphql-go/graphql"
5 | "rxdrag.com/entify/model/graph"
6 | "rxdrag.com/entify/model/meta"
7 | "rxdrag.com/entify/scalars"
8 | )
9 |
10 | func InputPropertyType(property graph.Propertier) graphql.Type {
11 | if property.GetType() == meta.FILE {
12 | return scalars.UploadType
13 | }
14 | return PropertyType(property)
15 | }
16 |
17 | func PropertyType(property graph.Propertier) graphql.Output {
18 | switch property.GetType() {
19 | case meta.ID:
20 | return graphql.ID
21 | case meta.INT:
22 | return graphql.Int
23 | case meta.FLOAT:
24 | return graphql.Float
25 | case meta.BOOLEAN:
26 | return graphql.Boolean
27 | case meta.STRING:
28 | return graphql.String
29 | case meta.DATE:
30 | return graphql.DateTime
31 | case meta.VALUE_OBJECT,
32 | meta.ENTITY,
33 | meta.ID_ARRAY,
34 | meta.INT_ARRAY,
35 | meta.FLOAT_ARRAY,
36 | meta.STRING_ARRAY,
37 | meta.DATE_ARRAY,
38 | meta.ENUM_ARRAY,
39 | meta.VALUE_OBJECT_ARRAY,
40 | meta.ENTITY_ARRAY:
41 | return scalars.JSONType
42 | case meta.ENUM:
43 | enum := property.GetEumnType()
44 | if enum == nil {
45 | panic("Can not find enum entity")
46 | }
47 | return Cache.EnumType(enum.Name)
48 | case meta.FILE:
49 | //return graphql.String
50 | return fileOutputType
51 | }
52 |
53 | panic("No column type:" + property.GetName())
54 | }
55 |
56 | func AttributeExp(column *graph.Attribute) *graphql.InputObjectFieldConfig {
57 | switch column.Type {
58 | case meta.INT:
59 | return &IntComparisonExp
60 | case meta.FLOAT:
61 | return &FloatComparisonExp
62 | case meta.BOOLEAN:
63 | return &BooleanComparisonExp
64 | case meta.STRING:
65 | return &StringComparisonExp
66 | case meta.DATE:
67 | return &DateTimeComparisonExp
68 | case meta.VALUE_OBJECT,
69 | meta.ENTITY,
70 | meta.ID_ARRAY,
71 | meta.INT_ARRAY,
72 | meta.FLOAT_ARRAY,
73 | meta.STRING_ARRAY,
74 | meta.DATE_ARRAY,
75 | meta.ENUM_ARRAY,
76 | meta.VALUE_OBJECT_ARRAY,
77 | meta.ENTITY_ARRAY,
78 | meta.FILE:
79 | return nil
80 | case meta.ID:
81 | return &IdComparisonExp
82 | case meta.ENUM:
83 | return EnumComparisonExp(column)
84 | }
85 |
86 | panic("No column type: " + column.Type)
87 | }
88 |
89 | func AttributeOrderBy(column *graph.Attribute) *graphql.Enum {
90 | switch column.Type {
91 | case meta.VALUE_OBJECT,
92 | meta.BOOLEAN,
93 | meta.ENTITY,
94 | meta.ID_ARRAY,
95 | meta.INT_ARRAY,
96 | meta.FLOAT_ARRAY,
97 | meta.STRING_ARRAY,
98 | meta.DATE_ARRAY,
99 | meta.ENUM_ARRAY,
100 | meta.VALUE_OBJECT_ARRAY,
101 | meta.ENTITY_ARRAY:
102 | return nil
103 | }
104 |
105 | return EnumOrderBy
106 | }
107 |
--------------------------------------------------------------------------------
/schema/query.go:
--------------------------------------------------------------------------------
1 | package schema
2 |
3 | import (
4 | "github.com/graphql-go/graphql"
5 | "rxdrag.com/entify/config"
6 | "rxdrag.com/entify/consts"
7 | "rxdrag.com/entify/model"
8 | "rxdrag.com/entify/model/graph"
9 | "rxdrag.com/entify/resolve"
10 | "rxdrag.com/entify/scalars"
11 | )
12 |
13 | func rootQuery() *graphql.Object {
14 | rootQueryConfig := graphql.ObjectConfig{
15 | Name: consts.ROOT_QUERY_NAME,
16 | Fields: queryFields(),
17 | }
18 |
19 | return graphql.NewObject(rootQueryConfig)
20 | }
21 |
22 | func queryFields() graphql.Fields {
23 | queryFields := graphql.Fields{
24 | consts.SERVICE: serviceField(),
25 | consts.ENTITIES: &graphql.Field{
26 | Type: &graphql.NonNull{
27 | OfType: &graphql.List{
28 | OfType: EntityType,
29 | },
30 | },
31 | Args: graphql.FieldConfigArgument{
32 | consts.REPRESENTATIONS: &graphql.ArgumentConfig{
33 | Type: &graphql.NonNull{
34 | OfType: &graphql.List{
35 | OfType: &graphql.NonNull{
36 | OfType: scalars.AnyType,
37 | },
38 | },
39 | },
40 | },
41 | },
42 | },
43 | }
44 |
45 | for _, intf := range model.GlobalModel.Graph.RootInterfaces() {
46 | appendInterfaceToQueryFields(intf, queryFields)
47 | }
48 |
49 | for _, entity := range model.GlobalModel.Graph.RootEnities() {
50 | appendEntityToQueryFields(entity, queryFields)
51 | }
52 | for _, partial := range model.GlobalModel.Graph.RootPartails() {
53 | appendPartailToQueryFields(partial, queryFields)
54 | }
55 | // for _, service := range model.GlobalModel.Graph.RootExternals() {
56 | // appendServiceQueryFields(service, queryFields)
57 | // }
58 |
59 | if config.AuthUrl() == "" {
60 | appendAuthToQuery(queryFields)
61 | }
62 |
63 | return queryFields
64 | }
65 |
66 | func queryResponseType(class *graph.Class) graphql.Output {
67 | return &graphql.NonNull{
68 | OfType: &graphql.List{
69 | OfType: Cache.OutputType(class.Name()),
70 | },
71 | }
72 | }
73 |
74 | func queryArgs(name string) graphql.FieldConfigArgument {
75 | config := graphql.FieldConfigArgument{
76 | consts.ARG_DISTINCTON: &graphql.ArgumentConfig{
77 | Type: Cache.DistinctOnEnum(name),
78 | },
79 | consts.ARG_LIMIT: &graphql.ArgumentConfig{
80 | Type: graphql.Int,
81 | },
82 | consts.ARG_OFFSET: &graphql.ArgumentConfig{
83 | Type: graphql.Int,
84 | },
85 | consts.ARG_WHERE: &graphql.ArgumentConfig{
86 | Type: Cache.WhereExp(name),
87 | },
88 | }
89 | orderByExp := Cache.OrderByExp(name)
90 |
91 | if orderByExp != nil {
92 | config[consts.ARG_ORDERBY] = &graphql.ArgumentConfig{
93 | Type: orderByExp,
94 | }
95 | }
96 | return config
97 | }
98 |
99 | func appendInterfaceToQueryFields(intf *graph.Interface, fields graphql.Fields) {
100 | (fields)[intf.QueryName()] = &graphql.Field{
101 | Type: queryResponseType(&intf.Class),
102 | Args: queryArgs(intf.Name()),
103 | Resolve: resolve.QueryInterfaceResolveFn(intf),
104 | }
105 | (fields)[intf.QueryOneName()] = &graphql.Field{
106 | Type: Cache.OutputType(intf.Name()),
107 | Args: queryArgs(intf.Name()),
108 | Resolve: resolve.QueryOneInterfaceResolveFn(intf),
109 | }
110 |
111 | (fields)[intf.QueryAggregateName()] = &graphql.Field{
112 | Type: AggregateInterfaceType(intf),
113 | Args: queryArgs(intf.Name()),
114 | Resolve: resolve.QueryInterfaceResolveFn(intf),
115 | }
116 | }
117 |
118 | func appendEntityToQueryFields(entity *graph.Entity, fields graphql.Fields) {
119 | (fields)[entity.QueryName()] = &graphql.Field{
120 | Type: queryResponseType(&entity.Class),
121 | Args: queryArgs(entity.Name()),
122 | Resolve: resolve.QueryEntityResolveFn(entity),
123 | }
124 | (fields)[entity.QueryOneName()] = &graphql.Field{
125 | Type: Cache.OutputType(entity.Name()),
126 | Args: queryArgs(entity.Name()),
127 | Resolve: resolve.QueryOneEntityResolveFn(entity),
128 | }
129 |
130 | if notSystemEntity(entity) {
131 | (fields)[entity.QueryAggregateName()] = &graphql.Field{
132 | Type: AggregateEntityType(entity),
133 | Args: queryArgs(entity.Name()),
134 | Resolve: resolve.QueryEntityResolveFn(entity),
135 | }
136 | }
137 | }
138 |
139 | func appendPartailToQueryFields(partial *graph.Partial, fields graphql.Fields) {
140 | (fields)[partial.QueryName()] = &graphql.Field{
141 | Type: queryResponseType(&partial.Class),
142 | Args: queryArgs(partial.Name()),
143 | Resolve: resolve.QueryEntityResolveFn(&partial.Entity),
144 | }
145 | (fields)[partial.QueryOneName()] = &graphql.Field{
146 | Type: Cache.OutputType(partial.Name()),
147 | Args: queryArgs(partial.Name()),
148 | Resolve: resolve.QueryOneEntityResolveFn(&partial.Entity),
149 | }
150 |
151 | (fields)[partial.QueryAggregateName()] = &graphql.Field{
152 | Type: AggregatePartialType(partial),
153 | Args: queryArgs(partial.Name()),
154 | Resolve: resolve.QueryEntityResolveFn(&partial.Entity),
155 | }
156 | }
157 |
158 | func appendAuthToQuery(fields graphql.Fields) {
159 | fields[consts.ME] = &graphql.Field{
160 | Type: baseUserType,
161 | Resolve: resolve.Me,
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/schema/queryargs.go:
--------------------------------------------------------------------------------
1 | package schema
2 |
3 | import (
4 | "github.com/graphql-go/graphql"
5 | "rxdrag.com/entify/consts"
6 | "rxdrag.com/entify/model"
7 | "rxdrag.com/entify/model/graph"
8 | )
9 |
10 | func (c *TypeCache) makeQueryArgs() {
11 | for i := range model.GlobalModel.Graph.Interfaces {
12 | c.makeOneInterfaceArgs(model.GlobalModel.Graph.Interfaces[i])
13 | }
14 | for i := range model.GlobalModel.Graph.Entities {
15 | c.makeOneEntityArgs(model.GlobalModel.Graph.Entities[i])
16 | }
17 | for i := range model.GlobalModel.Graph.Partials {
18 | c.makeOnePartailArgs(model.GlobalModel.Graph.Partials[i])
19 | }
20 |
21 | for i := range model.GlobalModel.Graph.Externals {
22 | c.makeOneExternalArgs(model.GlobalModel.Graph.Externals[i])
23 | }
24 | c.makeRelaionWhereExp()
25 | }
26 |
27 | func (c *TypeCache) makeOneEntityArgs(entity *graph.Entity) {
28 | c.makeOneArgs(entity.Name(), entity.AllAttributes())
29 | }
30 |
31 | func (c *TypeCache) makeOneInterfaceArgs(intf *graph.Interface) {
32 | c.makeOneArgs(intf.Name(), intf.AllAttributes())
33 | }
34 |
35 | func (c *TypeCache) makeOnePartailArgs(partial *graph.Partial) {
36 | c.makeOneArgs(partial.Name(), partial.AllAttributes())
37 | }
38 |
39 | func (c *TypeCache) makeOneExternalArgs(external *graph.External) {
40 | c.makeOneArgs(external.Name(), external.AllAttributes())
41 | }
42 |
43 | func (c *TypeCache) makeOneArgs(name string, attrs []*graph.Attribute) {
44 | whereExp := makeWhereExp(name, attrs)
45 | c.WhereExpMap[name] = whereExp
46 |
47 | orderByExp := makeOrderBy(name, attrs)
48 | if len(orderByExp.Fields()) > 0 {
49 | c.OrderByMap[name] = orderByExp
50 | }
51 |
52 | distinctOnEnum := makeDistinctOnEnum(name, attrs)
53 | c.DistinctOnEnumMap[name] = distinctOnEnum
54 | }
55 |
56 | func (c *TypeCache) makeRelaionWhereExp() {
57 | for className := range c.WhereExpMap {
58 | exp := c.WhereExpMap[className]
59 | intf := model.GlobalModel.Graph.GetInterfaceByName(className)
60 | entity := model.GlobalModel.Graph.GetEntityByName(className)
61 | partial := model.GlobalModel.Graph.GetPartialByName(className)
62 | external := model.GlobalModel.Graph.GetExternalByName(className)
63 | if intf == nil && entity == nil && partial == nil && external == nil {
64 | panic("Fatal error, can not find class by name:" + className)
65 | }
66 | var associations []*graph.Association
67 | if intf != nil {
68 | associations = intf.AllAssociations()
69 | } else if entity != nil {
70 | associations = entity.AllAssociations()
71 | } else if partial != nil {
72 | associations = partial.AllAssociations()
73 | } else if external != nil {
74 | associations = external.AllAssociations()
75 | }
76 | for i := range associations {
77 | assoc := associations[i]
78 | exp.AddFieldConfig(assoc.Name(), &graphql.InputObjectFieldConfig{
79 | Type: c.WhereExp(assoc.TypeClass().Name()),
80 | })
81 | }
82 | }
83 | }
84 |
85 | func makeWhereExp(name string, attrs []*graph.Attribute) *graphql.InputObject {
86 | expName := name + consts.BOOLEXP
87 | andExp := graphql.InputObjectFieldConfig{}
88 | notExp := graphql.InputObjectFieldConfig{}
89 | orExp := graphql.InputObjectFieldConfig{}
90 |
91 | fields := graphql.InputObjectConfigFieldMap{
92 | consts.ARG_AND: &andExp,
93 | consts.ARG_NOT: ¬Exp,
94 | consts.ARG_OR: &orExp,
95 | }
96 |
97 | boolExp := graphql.NewInputObject(
98 | graphql.InputObjectConfig{
99 | Name: expName,
100 | Fields: fields,
101 | },
102 | )
103 | andExp.Type = &graphql.List{
104 | OfType: &graphql.NonNull{
105 | OfType: boolExp,
106 | },
107 | }
108 | notExp.Type = boolExp
109 | orExp.Type = &graphql.List{
110 | OfType: &graphql.NonNull{
111 | OfType: boolExp,
112 | },
113 | }
114 |
115 | for i := range attrs {
116 | attr := attrs[i]
117 | columnExp := AttributeExp(attr)
118 |
119 | if columnExp != nil {
120 | fields[attr.Name] = columnExp
121 | }
122 | }
123 | return boolExp
124 | }
125 |
126 | func makeOrderBy(name string, attrs []*graph.Attribute) *graphql.InputObject {
127 | fields := graphql.InputObjectConfigFieldMap{}
128 |
129 | orderByExp := graphql.NewInputObject(
130 | graphql.InputObjectConfig{
131 | Name: name + consts.ORDERBY,
132 | Fields: fields,
133 | },
134 | )
135 |
136 | for i := range attrs {
137 | attr := attrs[i]
138 | attrOrderBy := AttributeOrderBy(attr)
139 | if attrOrderBy != nil {
140 | fields[attr.Name] = &graphql.InputObjectFieldConfig{Type: attrOrderBy}
141 | }
142 | }
143 | return orderByExp
144 | }
145 |
146 | func makeDistinctOnEnum(name string, attrs []*graph.Attribute) *graphql.Enum {
147 | enumValueConfigMap := graphql.EnumValueConfigMap{}
148 | for i := range attrs {
149 | attr := attrs[i]
150 | enumValueConfigMap[attr.Name] = &graphql.EnumValueConfig{
151 | Value: attr.Name,
152 | }
153 | }
154 |
155 | entEnum := graphql.NewEnum(
156 | graphql.EnumConfig{
157 | Name: name + consts.DISTINCTEXP,
158 | Values: enumValueConfigMap,
159 | },
160 | )
161 | return entEnum
162 | }
163 |
--------------------------------------------------------------------------------
/schema/relation.go:
--------------------------------------------------------------------------------
1 | package schema
2 |
3 | import (
4 | "github.com/graphql-go/graphql"
5 | "rxdrag.com/entify/model"
6 | "rxdrag.com/entify/model/graph"
7 | "rxdrag.com/entify/resolve"
8 | )
9 |
10 | func (c *TypeCache) makeRelations() {
11 | for i := range model.GlobalModel.Graph.Interfaces {
12 | intf := model.GlobalModel.Graph.Interfaces[i]
13 | interfaceType := c.InterfaceTypeMap[intf.Name()]
14 | if interfaceType == nil {
15 | panic("Can find object type:" + intf.Name())
16 | }
17 | for _, association := range intf.AllAssociations() {
18 | if interfaceType.Fields()[association.Name()] != nil {
19 | panic("Duplicate interface field: " + intf.Name() + "." + association.Name())
20 | }
21 | interfaceType.AddFieldConfig(association.Name(), &graphql.Field{
22 | Name: association.Name(),
23 | Type: c.AssociationType(association),
24 | Description: association.Description(),
25 | Resolve: resolve.QueryAssociationFn(association),
26 | Args: queryArgs(association.TypeClass().Name()),
27 | })
28 | }
29 | }
30 | for i := range model.GlobalModel.Graph.Entities {
31 | entity := model.GlobalModel.Graph.Entities[i]
32 | objectType := c.ObjectTypeMap[entity.Name()]
33 | for _, association := range entity.AllAssociations() {
34 | if objectType.Fields()[association.Name()] != nil {
35 | panic("Duplicate entity field: " + entity.Name() + "." + association.Name())
36 | }
37 | objectType.AddFieldConfig(association.Name(), &graphql.Field{
38 | Name: association.Name(),
39 | Type: c.AssociationType(association),
40 | Description: association.Description(),
41 | Resolve: resolve.QueryAssociationFn(association),
42 | Args: queryArgs(association.TypeClass().Name()),
43 | })
44 | }
45 | }
46 | }
47 |
48 | func (c *TypeCache) AssociationType(association *graph.Association) graphql.Output {
49 | if association.IsArray() {
50 | return &graphql.NonNull{
51 | OfType: &graphql.List{
52 | OfType: c.OutputType(association.TypeClass().Name()),
53 | },
54 | }
55 | } else {
56 | return c.OutputType(association.TypeClass().Name())
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/schema/resolve.go:
--------------------------------------------------------------------------------
1 | package schema
2 |
3 | import (
4 | "github.com/graphql-go/graphql"
5 | "rxdrag.com/entify/resolve"
6 | )
7 |
8 | func publishResolve(p graphql.ResolveParams) (interface{}, error) {
9 | result, err := resolve.PublishMetaResolve(p)
10 | if err != nil {
11 | return result, err
12 | }
13 |
14 | InitSchema()
15 | return result, nil
16 | }
17 |
18 | func installResolve(p graphql.ResolveParams) (interface{}, error) {
19 | result, err := resolve.InstallResolve(p)
20 | if err != nil {
21 | return result, err
22 | }
23 | InitSchema()
24 | return result, err
25 | }
26 |
--------------------------------------------------------------------------------
/schema/schema.go:
--------------------------------------------------------------------------------
1 | package schema
2 |
3 | import (
4 | "github.com/graphql-go/graphql"
5 | "rxdrag.com/entify/consts"
6 | "rxdrag.com/entify/model"
7 | "rxdrag.com/entify/scalars"
8 | "rxdrag.com/entify/utils"
9 | )
10 |
11 | func MakeSchema() {
12 | Cache.MakeCache()
13 |
14 | EntityType = graphql.NewUnion(
15 | graphql.UnionConfig{
16 | Name: consts.ENTITY_TYPE,
17 | Types: Cache.EntityObjects(),
18 | ResolveType: resolveTypeFn,
19 | },
20 | )
21 |
22 | schemaConfig := graphql.SchemaConfig{
23 | Query: rootQuery(),
24 | Mutation: rootMutation(),
25 | Subscription: RootSubscription(),
26 | Directives: []*graphql.Directive{
27 | graphql.NewDirective(graphql.DirectiveConfig{
28 | Name: consts.EXTERNAL,
29 | Locations: []string{graphql.DirectiveLocationField},
30 | }),
31 | graphql.NewDirective(graphql.DirectiveConfig{
32 | Name: consts.REQUIRES,
33 | Locations: []string{graphql.DirectiveLocationField},
34 | Args: graphql.FieldConfigArgument{
35 | consts.ARG_FIELDS: &graphql.ArgumentConfig{
36 | Type: &graphql.NonNull{
37 | OfType: scalars.FieldSetType,
38 | },
39 | },
40 | },
41 | }),
42 | graphql.NewDirective(graphql.DirectiveConfig{
43 | Name: consts.PROVIDES,
44 | Locations: []string{graphql.DirectiveLocationField},
45 | Args: graphql.FieldConfigArgument{
46 | consts.ARG_FIELDS: &graphql.ArgumentConfig{
47 | Type: &graphql.NonNull{
48 | OfType: scalars.FieldSetType,
49 | },
50 | },
51 | },
52 | }),
53 | graphql.NewDirective(graphql.DirectiveConfig{
54 | Name: consts.KEY,
55 | Locations: []string{graphql.DirectiveLocationObject, graphql.DirectiveLocationInterface},
56 | Args: graphql.FieldConfigArgument{
57 | consts.ARG_FIELDS: &graphql.ArgumentConfig{
58 | Type: &graphql.NonNull{
59 | OfType: scalars.FieldSetType,
60 | },
61 | },
62 | },
63 | }),
64 | graphql.NewDirective(graphql.DirectiveConfig{
65 | Name: consts.EXTENDS,
66 | Locations: []string{graphql.DirectiveLocationObject, graphql.DirectiveLocationInterface},
67 | }),
68 | },
69 | Types: append(Cache.EntityTypes(), scalars.FieldSetType),
70 | }
71 | theSchema, err := graphql.NewSchema(schemaConfig)
72 |
73 | if err != nil {
74 | panic(err)
75 | //log.Fatalf("failed to create new schema, error: %v", err)
76 | }
77 | model.GlobalModel.Schema = &theSchema
78 | }
79 |
80 | func ResolveSchema() *graphql.Schema {
81 | return model.GlobalModel.Schema
82 | }
83 |
84 | func InitSchema() {
85 | LoadModel()
86 | MakeSchema()
87 | }
88 |
89 | func InitAuthInstallSchema() {
90 | LoadModel()
91 | schemaConfig := graphql.SchemaConfig{
92 | Query: graphql.NewObject(
93 | graphql.ObjectConfig{
94 | Name: consts.ROOT_QUERY_NAME,
95 | Fields: graphql.Fields{
96 | consts.INSTALLED: &graphql.Field{
97 | Type: graphql.Boolean,
98 | Resolve: func(p graphql.ResolveParams) (interface{}, error) {
99 | defer utils.PrintErrorStack()
100 | return false, nil
101 | },
102 | },
103 | },
104 | },
105 | ),
106 | Mutation: graphql.NewObject(graphql.ObjectConfig{
107 | Name: consts.ROOT_MUTATION_NAME,
108 | Fields: graphql.Fields{
109 | "installAuth": &graphql.Field{
110 | Type: graphql.Boolean,
111 | Args: graphql.FieldConfigArgument{
112 | INPUT: &graphql.ArgumentConfig{
113 | Type: &graphql.NonNull{
114 | OfType: installInputType,
115 | },
116 | },
117 | },
118 | Resolve: installResolve,
119 | },
120 | },
121 | Description: "Root mutation of entity engine. For install auth entify",
122 | }),
123 | }
124 | theSchema, err := graphql.NewSchema(schemaConfig)
125 |
126 | if err != nil {
127 | panic(err)
128 | //log.Fatalf("failed to create new schema, error: %v", err)
129 | }
130 | model.GlobalModel.Schema = &theSchema
131 | }
132 |
--------------------------------------------------------------------------------
/schema/service.go:
--------------------------------------------------------------------------------
1 | package schema
2 |
3 | import (
4 | "github.com/graphql-go/graphql"
5 | "rxdrag.com/entify/model/graph"
6 | "rxdrag.com/entify/model/meta"
7 | "rxdrag.com/entify/utils"
8 | )
9 |
10 | func appendServiceQueryFields(exClass *graph.Entity, fields graphql.Fields) {
11 | methods := exClass.MethodsByType(meta.QUERY)
12 | if len(methods) > 0 {
13 | (fields)[exClass.QueryName()] = &graphql.Field{
14 | Type: graphql.String,
15 | //Resolve: resolve.QueryResolveFn(node),
16 | }
17 | }
18 |
19 | }
20 |
21 | func appendServiceMutationFields(serviceClass *graph.Entity, fields graphql.Fields) {
22 | methods := serviceClass.MethodsByType(meta.MUTATION)
23 | if len(methods) > 0 {
24 | (fields)[utils.FirstLower(serviceClass.Name())] = &graphql.Field{
25 | Type: graphql.String,
26 | //Resolve: resolve.QueryResolveFn(node),
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/schema/subscription.go:
--------------------------------------------------------------------------------
1 | package schema
2 |
3 | import (
4 | "fmt"
5 |
6 | "rxdrag.com/entify/consts"
7 |
8 | "github.com/graphql-go/graphql"
9 | )
10 |
11 | type Feed struct {
12 | ID string `graphql:"id"`
13 | }
14 |
15 | var FeedType = graphql.NewObject(graphql.ObjectConfig{
16 | Name: "FeedType",
17 | Fields: graphql.Fields{
18 | "id": &graphql.Field{
19 | Type: graphql.ID,
20 | },
21 | },
22 | })
23 |
24 | func RootSubscription() *graphql.Object {
25 | // subscriptionFields := graphql.Fields{"feed": &graphql.Field{
26 | // Type: FeedType,
27 | // Resolve: func(p graphql.ResolveParams) (interface{}, error) {
28 | // fmt.Println("Resolve", p.Source)
29 | // return p.Source, nil
30 | // },
31 | // Subscribe: func(p graphql.ResolveParams) (interface{}, error) {
32 | // fmt.Println("Subscribe it")
33 | // c := make(chan interface{})
34 |
35 | // go func() {
36 | // var i int
37 | // for {
38 | // i++
39 | // feed := Feed{ID: fmt.Sprintf("%d", i)}
40 | // select {
41 | // case <-p.Context.Done():
42 | // log.Println("[RootSubscription] [Subscribe] subscription canceled")
43 | // close(c)
44 | // return
45 | // default:
46 | // c <- feed
47 | // }
48 |
49 | // time.Sleep(250 * time.Millisecond)
50 |
51 | // if i == 21 {
52 | // close(c)
53 | // return
54 | // }
55 | // }
56 | // }()
57 |
58 | // return c, nil
59 | // },
60 | // }}
61 |
62 | subscriptionObj := graphql.NewObject(graphql.ObjectConfig{
63 | Name: consts.ROOT_SUBSCRIPTION_NAME,
64 | Fields: queryFields(),
65 | })
66 |
67 | //添加订阅代码
68 | fields := subscriptionObj.Fields()
69 | for i := range fields {
70 | field := fields[i]
71 | field.Subscribe = func(p graphql.ResolveParams) (interface{}, error) {
72 | fmt.Println("Resolve", p.Source)
73 | return p.Source, nil
74 | }
75 | }
76 |
77 | return subscriptionObj
78 | }
79 |
80 | // func appendToSubscriptionFields(node graph.Node, fields *graphql.Fields) {
81 |
82 | // (*fields)[utils.FirstLower(node.Name())] = &graphql.Field{
83 | // Type: queryResponseType(node),
84 | // Args: quryeArgs(node),
85 | // Resolve: func(p graphql.ResolveParams) (interface{}, error) {
86 | // return p.Source, nil
87 | // },
88 | // }
89 | // (*fields)[consts.ONE+node.Name()] = &graphql.Field{
90 | // Type: Cache.OutputType(node.Name()),
91 | // Args: quryeArgs(node),
92 | // Resolve: func(p graphql.ResolveParams) (interface{}, error) {
93 | // return p.Source, nil
94 | // },
95 | // }
96 |
97 | // (*fields)[utils.FirstLower(node.Name())+utils.FirstUpper(consts.AGGREGATE)] = &graphql.Field{
98 | // Type: *AggregateType(node),
99 | // Args: quryeArgs(node),
100 | // Resolve: func(p graphql.ResolveParams) (interface{}, error) {
101 | // return p.Source, nil
102 | // },
103 | // }
104 | // }
105 |
--------------------------------------------------------------------------------
/schema/upload.go:
--------------------------------------------------------------------------------
1 | package schema
2 |
--------------------------------------------------------------------------------
/sdlview.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | SDL格式化
7 |
8 |
9 |
10 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
SDL格式化
57 |
58 |
59 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/storage/file.go:
--------------------------------------------------------------------------------
1 | package storage
2 |
3 | import (
4 | "fmt"
5 | "io"
6 | "mime/multipart"
7 | "os"
8 | "path/filepath"
9 |
10 | "github.com/google/uuid"
11 | "rxdrag.com/entify/consts"
12 | )
13 |
14 | type File struct {
15 | File multipart.File
16 | Filename string
17 | Size int64
18 | }
19 |
20 | type FileInfo struct {
21 | Path string `json:"path"`
22 | Filename string `json:"fileName"`
23 | Size int64 `json:"size"`
24 | MimeType string `json:"mimeType"`
25 | ExtName string `json:"extName"`
26 | }
27 |
28 | var mimeTypes = map[string]string{
29 | ".css": "text/css; charset=utf-8",
30 | ".gif": "image/gif",
31 | ".htm": "text/html; charset=utf-8",
32 | ".html": "text/html; charset=utf-8",
33 | ".jpg": "image/jpeg",
34 | ".js": "application/x-javascript",
35 | ".pdf": "application/pdf",
36 | ".png": "image/png",
37 | ".xml": "text/xml; charset=utf-8",
38 | }
39 |
40 | func (f *File) extName() string {
41 | return filepath.Ext(f.Filename)
42 | }
43 |
44 | func (f *File) mimeType() string {
45 | //mtype, err := mimetype.DetectReader(f.File)
46 |
47 | return mimeTypes[f.extName()]
48 | }
49 |
50 | func (f *File) Save() FileInfo {
51 | name := fmt.Sprintf("%s%s", uuid.New().String(), f.extName())
52 | localPath := fmt.Sprintf("%s/%s", consts.UPLOAD_PATH, name)
53 | file, err := os.OpenFile(
54 | localPath,
55 | os.O_WRONLY|os.O_CREATE,
56 | 0666,
57 | )
58 | defer file.Close()
59 | if err != nil {
60 | panic(err.Error())
61 | }
62 | io.Copy(file, f.File)
63 | return FileInfo{Path: name, Filename: f.Filename, Size: f.Size, MimeType: f.mimeType(), ExtName: f.extName()}
64 | }
65 |
--------------------------------------------------------------------------------
/utils/array.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | func StringFilter(arr []string, f func(value string) bool) []string {
4 | positive := []string{}
5 |
6 | for i := range arr {
7 | if f(arr[i]) {
8 | positive = append(positive, arr[i])
9 | }
10 | }
11 |
12 | return positive
13 | }
14 |
--------------------------------------------------------------------------------
/utils/debug.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "runtime/debug"
7 | )
8 |
9 | func PrintErrorStack() {
10 | if x := recover(); x != nil {
11 | println(fmt.Sprintf("%T: %+v", x, x))
12 | log.Printf("%s\n", debug.Stack())
13 | panic(x)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/utils/id.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import "rxdrag.com/entify/config"
4 |
5 | const (
6 | SERVICE_BITS = 52
7 | ENTITY_ID_BITS = 32
8 | )
9 |
10 | func EncodeBaseId(entityInnerId uint64) uint64 {
11 | return uint64(config.ServiceId())<
> ENTITY_ID_BITS
16 | }
17 |
--------------------------------------------------------------------------------
/utils/json.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "database/sql/driver"
5 | "encoding/json"
6 | "errors"
7 | )
8 |
9 | type JSON map[string]interface{}
10 |
11 | func (m JSON) Value() (driver.Value, error) {
12 | if len(m) == 0 {
13 | return nil, nil
14 | }
15 | j, err := json.Marshal(m)
16 | if err != nil {
17 | return nil, err
18 | }
19 | return driver.Value([]byte(j)), nil
20 | }
21 |
22 | func (m *JSON) Scan(src interface{}) error {
23 | var source []byte
24 | _m := make(map[string]interface{})
25 |
26 | switch src.(type) {
27 | case []uint8:
28 | source = []byte(src.([]uint8))
29 | case nil:
30 | return nil
31 | default:
32 | return errors.New("incompatible type for JSON")
33 | }
34 | err := json.Unmarshal(source, &_m)
35 | if err != nil {
36 | return err
37 | }
38 | *m = JSON(_m)
39 | return nil
40 | }
41 |
--------------------------------------------------------------------------------
/utils/map.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | func MapStringKeys(m map[string]interface{}, wapper string) []string {
4 | keys := make([]string, 0, len(m))
5 | for k := range m {
6 | keys = append(keys, wapper+k+wapper)
7 | }
8 | return keys
9 | }
10 |
11 | func MapValues(m map[string]interface{}, wapper string) []interface{} {
12 | values := make([]interface{}, 0, len(m))
13 | for k := range m {
14 | values = append(values, m[k])
15 | }
16 | return values
17 | }
18 |
--------------------------------------------------------------------------------
/utils/object.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | type Object = map[string]interface{}
4 |
--------------------------------------------------------------------------------
/utils/string.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "strings"
5 | )
6 |
7 | // FirstUpper 字符串首字母大写
8 | func FirstUpper(s string) string {
9 | if s == "" {
10 | return ""
11 | }
12 | return strings.ToUpper(s[:1]) + s[1:]
13 | }
14 |
15 | // FirstLower 字符串首字母小写
16 | func FirstLower(s string) string {
17 | if s == "" {
18 | return ""
19 | }
20 | return strings.ToLower(s[:1]) + s[1:]
21 | }
22 |
23 | /**
24 | * 驼峰转蛇形 snake string
25 | * @description XxYy to xx_yy , XxYY to xx_y_y
26 | * @date 2020/7/30
27 | * @param s 需要转换的字符串
28 | * @return string
29 | **/
30 | func SnakeString(s string) string {
31 | data := make([]byte, 0, len(s)*2)
32 | j := false
33 | num := len(s)
34 | for i := 0; i < num; i++ {
35 | d := s[i]
36 | // or通过ASCII码进行大小写的转化
37 | // 65-90(A-Z),97-122(a-z)
38 | //判断如果字母为大写的A-Z就在前面拼接一个_
39 | if i > 0 && d >= 'A' && d <= 'Z' && j {
40 | data = append(data, '_')
41 | }
42 | if d != '_' {
43 | j = true
44 | }
45 | data = append(data, d)
46 | }
47 | //ToLower把大写字母统一转小写
48 | return strings.ToLower(string(data[:]))
49 | }
50 |
51 | /**
52 | * 蛇形转驼峰
53 | * @description xx_yy to XxYx xx_y_y to XxYY
54 | * @date 2020/7/30
55 | * @param s要转换的字符串
56 | * @return string
57 | **/
58 | func CamelString(s string) string {
59 | data := make([]byte, 0, len(s))
60 | j := false
61 | k := false
62 | num := len(s) - 1
63 | for i := 0; i <= num; i++ {
64 | d := s[i]
65 | if k == false && d >= 'A' && d <= 'Z' {
66 | k = true
67 | }
68 | if d >= 'a' && d <= 'z' && (j || k == false) {
69 | d = d - 32
70 | j = false
71 | k = true
72 | }
73 | if k && d == '_' && num > i && s[i+1] >= 'a' && s[i+1] <= 'z' {
74 | j = true
75 | continue
76 | }
77 | data = append(data, d)
78 | }
79 | return string(data[:])
80 | }
81 |
--------------------------------------------------------------------------------
/备忘.md:
--------------------------------------------------------------------------------
1 | docker内连宿主机mysql host.docker.internal
2 |
3 | 创建镜像:
4 | docker build --pull --rm -f "Dockerfile" -t entify:lastest "."
5 |
6 | 创建容器
7 | docker create -p 4000:4000 --name entify entify:lastest
8 |
9 | 安装权限模块指令:
10 |
11 | mutation{
12 | installAuth(
13 | input:{
14 | admin:"admin",
15 | password:"123456",
16 | }
17 | )
18 | }
19 |
20 | 用新的数据库创建一个容器
21 | docker create -p 4001:4000 -e SERVICE_ID=1 -e DATABASE="entify1" -e HOST="host.docker.internal" -e PORT="3306" -e USER="root" -e PASSWORD="RxDragDb" -e AUTH_URL="http://host.docker.internal:4000/graphql" -e storage="local" --name entify1 entify:lastest
--------------------------------------------------------------------------------