package s3browser import ( "fmt" "path/filepath" "time" "github.com/graph-gophers/dataloader" "github.com/graphql-go/graphql" "github.com/graphql-go/graphql/language/ast" ) type LoginResult struct { Token string `json:"token"` Successful bool `json:"successful"` } var graphqlDirType *graphql.Object var graphqlFileType *graphql.Object var graphqlLoginResultType *graphql.Object // 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: return valueAST.Value } return nil }, }) graphqlDirType = graphql.NewObject(graphql.ObjectConfig{ Name: "Directory", Description: "Represents a directory", Fields: graphql.Fields{ "id": &graphql.Field{ Type: graphql.NewNonNull(graphql.ID), }, "name": &graphql.Field{ Type: graphql.String, Resolve: func(p graphql.ResolveParams) (interface{}, error) { source, ok := p.Source.(Directory) if !ok { return nil, fmt.Errorf("Failed to parse source for resolve") } return filepath.Base(source.ID), nil }, }, }, }) graphqlFileType = graphql.NewObject(graphql.ObjectConfig{ Name: "File", Description: "Represents a file, not a directory", Fields: graphql.Fields{ "id": &graphql.Field{ Type: graphql.NewNonNull(graphql.ID), 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.(File) if !ok { return nil, fmt.Errorf("Failed to parse source for resolve") } return filepath.Base(source.ID), 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.(File) if !ok { return nil, fmt.Errorf("Failed to parse Source for parent resolve") } basename := getPathFromId(source.ID) return Directory{ ID: basename, }, nil }, }, }, }) graphqlDirType.AddFieldConfig("files", &graphql.Field{ Type: graphql.NewList(graphqlFileType), Resolve: func(p graphql.ResolveParams) (interface{}, error) { source, ok := p.Source.(Directory) if !ok { return nil, fmt.Errorf("Failed to parse Source for files resolve") } loader := p.Context.Value("loader").(map[string]*dataloader.Loader) thunk := loader["getFiles"].Load(p.Context, dataloader.StringKey(nomalizeID(source.ID))) return thunk() }, }) graphqlDirType.AddFieldConfig("directorys", &graphql.Field{ Type: graphql.NewList(graphqlDirType), Resolve: func(p graphql.ResolveParams) (interface{}, error) { source, ok := p.Source.(Directory) if !ok { return nil, fmt.Errorf("Failed to parse Source for directories resolve") } loader := p.Context.Value("loader").(map[string]*dataloader.Loader) thunk := loader["getDirs"].Load(p.Context, dataloader.StringKey(nomalizeID(source.ID))) return thunk() }, }) graphqlDirType.AddFieldConfig("parent", &graphql.Field{ Type: graphqlDirType, Resolve: func(p graphql.ResolveParams) (interface{}, error) { source, ok := p.Source.(Directory) if !ok { return nil, fmt.Errorf("Failed to parse Source for directories resolve") } return Directory{ ID: 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", }, }, }) } // graphqlTypes helper func for using the dataloader to get a file func loadFile(p graphql.ResolveParams) (*File, error) { source, ok := p.Source.(File) if !ok { return nil, fmt.Errorf("Failed to parse source for resolve") } loader := p.Context.Value("loader").(map[string]*dataloader.Loader) thunk := loader["getFile"].Load(p.Context, dataloader.StringKey(nomalizeID(source.ID))) result, err := thunk() if err != nil { return nil, err } file, ok := result.(*File) if !ok { return nil, fmt.Errorf("Failed to load file") } return file, err }