package gql import ( "fmt" "path/filepath" "time" "github.com/graphql-go/graphql" "github.com/graphql-go/graphql/language/ast" helper "git.kapelle.org/niklas/s3browser/internal/helper" "git.kapelle.org/niklas/s3browser/internal/loader" types "git.kapelle.org/niklas/s3browser/internal/types" ) var graphqlDirType *graphql.Object var graphqlFileType *graphql.Object var graphqlLoginResultType *graphql.Object var objIDType *graphql.Scalar //GraphqlTypes create all graphql types and stores the in the global variables func GraphqlTypes() { var dateTimeType = graphql.NewScalar(graphql.ScalarConfig{ Name: "DateTime", Description: "DateTime is a DateTime in ISO 8601 format", Serialize: func(value interface{}) interface{} { switch value := value.(type) { case time.Time: return value.Format(time.RFC3339) } return "INVALID" }, ParseValue: func(value interface{}) interface{} { switch tvalue := value.(type) { case string: if tval, err := time.Parse(time.RFC3339, tvalue); err != nil { return nil } else { return tval } } return nil }, ParseLiteral: func(valueAST ast.Value) interface{} { switch valueAST := valueAST.(type) { case *ast.StringValue: if tval, err := time.Parse(time.RFC3339, valueAST.Value); err != nil { return nil } else { return tval } } return nil }, }) objIDType = graphql.NewScalar(graphql.ScalarConfig{ Name: "objID", Description: `String representing a bucket, key and version combination. Looks like this: "bucketName:/name/of/key" or "bucketName@version:/name/of/key"`, Serialize: func(value interface{}) interface{} { switch value := value.(type) { case types.ID: return value.String() } return "INVALID" }, ParseValue: func(value interface{}) interface{} { switch tvalue := value.(type) { case string: return types.ParseID(tvalue) } return nil }, ParseLiteral: func(valueAST ast.Value) interface{} { switch valueAST := valueAST.(type) { case *ast.StringValue: return types.ParseID(valueAST.Value) } return nil }, }) graphqlDirType = graphql.NewObject(graphql.ObjectConfig{ Name: "Directory", Description: "Represents a directory", Fields: graphql.Fields{ "id": &graphql.Field{ Type: graphql.NewNonNull(objIDType), }, "name": &graphql.Field{ Type: graphql.String, Resolve: func(p graphql.ResolveParams) (interface{}, error) { source, ok := p.Source.(types.Directory) if !ok { return nil, fmt.Errorf("Failed to parse source for resolve") } return filepath.Base(source.ID.Key), nil }, }, }, }) graphqlFileType = graphql.NewObject(graphql.ObjectConfig{ Name: "File", Description: "Represents a file, not a directory", Fields: graphql.Fields{ "id": &graphql.Field{ Type: graphql.NewNonNull(objIDType), Description: "The uniqe ID of the file. Represents the path and the s3 key.", }, "name": &graphql.Field{ Type: graphql.String, Resolve: func(p graphql.ResolveParams) (interface{}, error) { source, ok := p.Source.(types.File) if !ok { return nil, fmt.Errorf("Failed to parse source for resolve") } return filepath.Base(source.ID.Key), nil }, }, "size": &graphql.Field{ Type: graphql.NewNonNull(graphql.Int), Resolve: func(p graphql.ResolveParams) (interface{}, error) { file, err := loadFile(p) if err != nil { return nil, err } return file.Size, nil }, }, "contentType": &graphql.Field{ Type: graphql.String, Resolve: func(p graphql.ResolveParams) (interface{}, error) { file, err := loadFile(p) if err != nil { return nil, err } return file.ContentType, nil }, }, "etag": &graphql.Field{ Type: graphql.String, Resolve: func(p graphql.ResolveParams) (interface{}, error) { file, err := loadFile(p) if err != nil { return nil, err } return file.ETag, nil }, }, "lastModified": &graphql.Field{ Type: dateTimeType, Resolve: func(p graphql.ResolveParams) (interface{}, error) { file, err := loadFile(p) if err != nil { return nil, err } return file.LastModified, nil }, }, "parent": &graphql.Field{ Type: graphqlDirType, Resolve: func(p graphql.ResolveParams) (interface{}, error) { source, ok := p.Source.(types.File) if !ok { return nil, fmt.Errorf("Failed to parse Source for parent resolve") } parent := source.ID.Parent() if parent == nil { return nil, nil } return types.Directory{ ID: *source.ID.Parent(), }, nil }, }, }, }) graphqlDirType.AddFieldConfig("files", &graphql.Field{ Type: graphql.NewList(graphqlFileType), Resolve: func(p graphql.ResolveParams) (interface{}, error) { source, ok := p.Source.(types.Directory) if !ok { return nil, fmt.Errorf("Failed to parse Source for files resolve") } loader := p.Context.Value("loader").(*loader.Loader) return loader.GetFiles(p.Context, source.ID) }, }) graphqlDirType.AddFieldConfig("directorys", &graphql.Field{ Type: graphql.NewList(graphqlDirType), Resolve: func(p graphql.ResolveParams) (interface{}, error) { source, ok := p.Source.(types.Directory) if !ok { return nil, fmt.Errorf("Failed to parse Source for directories resolve") } loader := p.Context.Value("loader").(*loader.Loader) return loader.GetDirs(p.Context, source.ID) }, }) graphqlDirType.AddFieldConfig("parent", &graphql.Field{ Type: graphqlDirType, Resolve: func(p graphql.ResolveParams) (interface{}, error) { source, ok := p.Source.(types.Directory) if !ok { return nil, fmt.Errorf("Failed to parse Source for directories resolve") } return types.Directory{ ID: helper.GetParentDir(source.ID), }, nil }, }) graphqlLoginResultType = graphql.NewObject(graphql.ObjectConfig{ Name: "LoginResut", Description: "Result of a login", Fields: graphql.Fields{ "token": &graphql.Field{ Type: graphql.String, Description: "JWT token if login was successful", }, "successful": &graphql.Field{ Type: graphql.NewNonNull(graphql.Boolean), Description: "If the login was successful", }, }, }) } //loadFile helper func for using the dataloader to get a file func loadFile(p graphql.ResolveParams) (*types.File, error) { source, ok := p.Source.(types.File) if !ok { return nil, fmt.Errorf("Failed to parse source for resolve") } loader := p.Context.Value("loader").(*loader.Loader) file, err := loader.GetFile(p.Context, source.ID) if err != nil { return nil, err } if !ok { return nil, fmt.Errorf("Failed to load file") } return file, err }