From b04a8fc024a18ce8248613d9cc7cd1fd6080ee41 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Fri, 27 Nov 2020 11:42:16 -0500 Subject: [PATCH] API: design schema for custom readmes --- api/graph/api/generated.go | 267 ++++++++++++++++++++++++++++++++++ api/graph/model/repository.go | 2 + api/graph/schema.graphqls | 14 ++ api/graph/schema.resolvers.go | 8 + 4 files changed, 291 insertions(+) diff --git a/api/graph/api/generated.go b/api/graph/api/generated.go index 8714f70..784e975 100644 --- a/api/graph/api/generated.go +++ b/api/graph/api/generated.go @@ -113,7 +113,9 @@ type ComplexityRoot struct { DeleteACL func(childComplexity int, id int) int DeleteArtifact func(childComplexity int, id int) int DeleteRepository func(childComplexity int, id int) int + RemoveReadme func(childComplexity int, repoID int) int UpdateACL func(childComplexity int, repoID int, mode model.AccessMode, entity string) int + UpdateReadme func(childComplexity int, repoID int, html string) int UpdateRepository func(childComplexity int, id int, input map[string]interface{}) int UploadArtifact func(childComplexity int, repoID int, revspec string, file graphql.Upload) int } @@ -151,6 +153,7 @@ type ComplexityRoot struct { Objects func(childComplexity int, ids []string) int Owner func(childComplexity int) int Path func(childComplexity int, revspec *string, path string) int + Readme func(childComplexity int) int References func(childComplexity int, cursor *model1.Cursor) int RevparseSingle func(childComplexity int, revspec string) int Updated func(childComplexity int) int @@ -248,6 +251,8 @@ type MutationResolver interface { DeleteACL(ctx context.Context, id int) (*model.ACL, error) UploadArtifact(ctx context.Context, repoID int, revspec string, file graphql.Upload) (*model.Artifact, error) DeleteArtifact(ctx context.Context, id int) (*model.Artifact, error) + UpdateReadme(ctx context.Context, repoID int, html string) (string, error) + RemoveReadme(ctx context.Context, repoID int) (*string, error) } type QueryResolver interface { Version(ctx context.Context) (*model.Version, error) @@ -566,6 +571,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.DeleteRepository(childComplexity, args["id"].(int)), true + case "Mutation.removeREADME": + if e.complexity.Mutation.RemoveReadme == nil { + break + } + + args, err := ec.field_Mutation_removeREADME_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.RemoveReadme(childComplexity, args["repoId"].(int)), true + case "Mutation.updateACL": if e.complexity.Mutation.UpdateACL == nil { break @@ -578,6 +595,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.UpdateACL(childComplexity, args["repoId"].(int), args["mode"].(model.AccessMode), args["entity"].(string)), true + case "Mutation.updateREADME": + if e.complexity.Mutation.UpdateReadme == nil { + break + } + + args, err := ec.field_Mutation_updateREADME_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.UpdateReadme(childComplexity, args["repoId"].(int), args["html"].(string)), true + case "Mutation.updateRepository": if e.complexity.Mutation.UpdateRepository == nil { break @@ -813,6 +842,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Repository.Path(childComplexity, args["revspec"].(*string), args["path"].(string)), true + case "Repository.readme": + if e.complexity.Repository.Readme == nil { + break + } + + return e.complexity.Repository.Readme(childComplexity), true + case "Repository.references": if e.complexity.Repository.References == nil { break @@ -1331,6 +1367,12 @@ type Repository { description: String visibility: Visibility! + # The repository's custom README, if set. + # + # NOTICE: This returns unsanitized HTML. It is the client's responsibility to + # sanitize this for display on the web, if so desired. + readme: String + # If this repository was cloned from another, this is set to the original # clone URL. upstreamUrl: String @@ -1607,6 +1649,14 @@ type Mutation { # Deletes an artifact. deleteArtifact(id: Int!): Artifact! @access(scope: OBJECTS, kind: RW) + + # Updates the custom README associated with this repository. Note that the + # provided HTML will be sanitized when displayed on the web; see + # https://man.sr.ht/markdown/#post-processing + updateREADME(repoId: Int!, html: String!): String! @access(scope: REPOSITORIES, kind: RW) + + # Removes the custom README associated with this repository. + removeREADME(repoId: Int!): String @access(scope: REPOSITORIES, kind: RW) } `, BuiltIn: false}, } @@ -1733,6 +1783,21 @@ func (ec *executionContext) field_Mutation_deleteRepository_args(ctx context.Con return args, nil } +func (ec *executionContext) field_Mutation_removeREADME_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 int + if tmp, ok := rawArgs["repoId"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("repoId")) + arg0, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["repoId"] = arg0 + return args, nil +} + func (ec *executionContext) field_Mutation_updateACL_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -1766,6 +1831,30 @@ func (ec *executionContext) field_Mutation_updateACL_args(ctx context.Context, r return args, nil } +func (ec *executionContext) field_Mutation_updateREADME_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 int + if tmp, ok := rawArgs["repoId"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("repoId")) + arg0, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["repoId"] = arg0 + var arg1 string + if tmp, ok := rawArgs["html"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("html")) + arg1, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["html"] = arg1 + return args, nil +} + func (ec *executionContext) field_Mutation_updateRepository_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -3772,6 +3861,143 @@ func (ec *executionContext) _Mutation_deleteArtifact(ctx context.Context, field return ec.marshalNArtifact2ᚖgitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐArtifact(ctx, field.Selections, res) } +func (ec *executionContext) _Mutation_updateREADME(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + IsResolver: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Mutation_updateREADME_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().UpdateReadme(rctx, args["repoId"].(int), args["html"].(string)) + } + directive1 := func(ctx context.Context) (interface{}, error) { + scope, err := ec.unmarshalNAccessScope2gitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐAccessScope(ctx, "REPOSITORIES") + if err != nil { + return nil, err + } + kind, err := ec.unmarshalNAccessKind2gitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐAccessKind(ctx, "RW") + if err != nil { + return nil, err + } + if ec.directives.Access == nil { + return nil, errors.New("directive access is not implemented") + } + return ec.directives.Access(ctx, nil, directive0, scope, kind) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(string); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be string`, tmp) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) _Mutation_removeREADME(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + IsResolver: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Mutation_removeREADME_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().RemoveReadme(rctx, args["repoId"].(int)) + } + directive1 := func(ctx context.Context) (interface{}, error) { + scope, err := ec.unmarshalNAccessScope2gitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐAccessScope(ctx, "REPOSITORIES") + if err != nil { + return nil, err + } + kind, err := ec.unmarshalNAccessKind2gitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐAccessKind(ctx, "RW") + if err != nil { + return nil, err + } + if ec.directives.Access == nil { + return nil, errors.New("directive access is not implemented") + } + return ec.directives.Access(ctx, nil, directive0, scope, kind) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(*string); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be *string`, tmp) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + func (ec *executionContext) _Query_version(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -4757,6 +4983,38 @@ func (ec *executionContext) _Repository_visibility(ctx context.Context, field gr return ec.marshalNVisibility2gitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐVisibility(ctx, field.Selections, res) } +func (ec *executionContext) _Repository_readme(ctx context.Context, field graphql.CollectedField, obj *model.Repository) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Repository", + Field: field, + Args: nil, + IsMethod: false, + IsResolver: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Readme, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + func (ec *executionContext) _Repository_upstreamUrl(ctx context.Context, field graphql.CollectedField, obj *model.Repository) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -8426,6 +8684,13 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { invalids++ } + case "updateREADME": + out.Values[i] = ec._Mutation_updateREADME(ctx, field) + if out.Values[i] == graphql.Null { + invalids++ + } + case "removeREADME": + out.Values[i] = ec._Mutation_removeREADME(ctx, field) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -8679,6 +8944,8 @@ func (ec *executionContext) _Repository(ctx context.Context, sel ast.SelectionSe if out.Values[i] == graphql.Null { atomic.AddUint32(&invalids, 1) } + case "readme": + out.Values[i] = ec._Repository_readme(ctx, field, obj) case "upstreamUrl": out.Values[i] = ec._Repository_upstreamUrl(ctx, field, obj) case "accessControlList": diff --git a/api/graph/model/repository.go b/api/graph/model/repository.go index 69b7b34..cd36901 100644 --- a/api/graph/model/repository.go +++ b/api/graph/model/repository.go @@ -22,6 +22,7 @@ type Repository struct { Name string `json:"name"` Description *string `json:"description"` UpstreamURL *string `json:"upstreamUrl"` + Readme *string `json:"readme"` Path string OwnerID int @@ -96,6 +97,7 @@ func (r *Repository) Fields() *database.ModelFields { { "description", "description", &r.Description }, { "visibility", "visibility", &r.RawVisibility }, { "upstream_uri", "upstreamUrl", &r.UpstreamURL }, + { "readme", "readme", &r.Readme }, // Always fetch: { "id", "", &r.ID }, diff --git a/api/graph/schema.graphqls b/api/graph/schema.graphqls index 260b70e..1ab5867 100644 --- a/api/graph/schema.graphqls +++ b/api/graph/schema.graphqls @@ -84,6 +84,12 @@ type Repository { description: String visibility: Visibility! + # The repository's custom README, if set. + # + # NOTICE: This returns unsanitized HTML. It is the client's responsibility to + # sanitize this for display on the web, if so desired. + readme: String + # If this repository was cloned from another, this is set to the original # clone URL. upstreamUrl: String @@ -360,4 +366,12 @@ type Mutation { # Deletes an artifact. deleteArtifact(id: Int!): Artifact! @access(scope: OBJECTS, kind: RW) + + # Updates the custom README associated with this repository. Note that the + # provided HTML will be sanitized when displayed on the web; see + # https://man.sr.ht/markdown/#post-processing + updateREADME(repoId: Int!, html: String!): String! @access(scope: REPOSITORIES, kind: RW) + + # Removes the custom README associated with this repository. + removeREADME(repoId: Int!): String @access(scope: REPOSITORIES, kind: RW) } diff --git a/api/graph/schema.resolvers.go b/api/graph/schema.resolvers.go index 3f73054..0cfc261 100644 --- a/api/graph/schema.resolvers.go +++ b/api/graph/schema.resolvers.go @@ -392,6 +392,14 @@ func (r *mutationResolver) DeleteArtifact(ctx context.Context, id int) (*model.A panic(fmt.Errorf("deleteArtifact: not implemented")) } +func (r *mutationResolver) UpdateReadme(ctx context.Context, repoID int, html string) (string, error) { + panic(fmt.Errorf("not implemented")) +} + +func (r *mutationResolver) RemoveReadme(ctx context.Context, repoID int) (*string, error) { + panic(fmt.Errorf("not implemented")) +} + func (r *queryResolver) Version(ctx context.Context) (*model.Version, error) { return &model.Version{ Major: 0, -- 2.38.4