Skip to content
This repository was archived by the owner on Sep 30, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions cmd/frontend/graphqlbackend/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -6833,10 +6833,19 @@ type SiteConfigurationChange implements Node {
via an internal process (example: site startup or SITE_CONFIG_FILE being reloaded).
"""
author: User

"""
A flag to indicate if the diff of changes to the previous site configuration
was reproduced or not. Sometimes it may not be possible to generate a diff
(for example the redacted contents are not available) in which case the
value of the flag will be false.
"""
reproducedDiff: Boolean!

"""
The diff string when diffed against the site config at previousID.
The diff string when diffed against the previous site config. When this is null there was no diff to the previous change.
"""
diff: String!
diff: String
"""
The timestamp when this change in the site config was applied.
"""
Expand Down
65 changes: 50 additions & 15 deletions cmd/frontend/graphqlbackend/site_config_change.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@ package graphqlbackend

import (
"context"
"fmt"

"github.com/graph-gophers/graphql-go"
"github.com/graph-gophers/graphql-go/relay"
"github.com/sourcegraph/sourcegraph/internal/database"
"github.com/sourcegraph/sourcegraph/internal/gqlutil"

"github.com/hexops/gotextdiff"
"github.com/hexops/gotextdiff/myers"
)

const siteConfigurationChangeKind = "SiteConfigurationChange"
Expand All @@ -21,11 +25,6 @@ func (r SiteConfigurationChangeResolver) ID() graphql.ID {
return marshalSiteConfigurationChangeID(r.siteConfig.ID)
}

// One line wrapper to be able to use in tests as well.
func marshalSiteConfigurationChangeID(id int32) graphql.ID {
return relay.MarshalID(siteConfigurationChangeKind, &id)
}

Comment on lines -24 to -28
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No change. Just moved it out of the way of the API methods and moved it all the way to the end of the file.

func (r SiteConfigurationChangeResolver) Author(ctx context.Context) (*UserResolver, error) {
if r.siteConfig.AuthorUserID == 0 {
return nil, nil
Expand All @@ -39,17 +38,48 @@ func (r SiteConfigurationChangeResolver) Author(ctx context.Context) (*UserResol
return user, nil
}

// TODO: Implement this.
func (r SiteConfigurationChangeResolver) Diff() string {
// TODO: We will do something like this, but for now return an empty string to not leak secrets
// until we have implemented redaction.
//
// if r.previousSiteConfig == nil {
// return ""
// }
func (r SiteConfigurationChangeResolver) ReproducedDiff() bool {
// If we were able to redact contents for both siteConfig and previousSiteConfig and store it in
// the DB, then we can generate a diff.
if r.siteConfig != nil {
// As a special case, if previousSiteConfig is nil (first entry in the DB) and we also have
// redactedContents available for this siteConfig, we can generate a diff (it will be all
// lines added).
if r.previousSiteConfig == nil && r.siteConfig.RedactedContents != "" {
return true
}

if r.siteConfig.RedactedContents != "" && r.previousSiteConfig.RedactedContents != "" {
return true
}
}

return false
}

func (r SiteConfigurationChangeResolver) Diff() *string {
if !r.ReproducedDiff() {
return nil
}

var prevID int32
var prevRedactedContents string
if r.previousSiteConfig != nil {
prevID = r.previousSiteConfig.ID

// 🚨 SECURITY: This should always use "siteConfig.RedactedContents" and never
// "siteConfig.Contents" to generate the diff because we do not want to leak secrets in the
// diff.
prevRedactedContents = r.previousSiteConfig.RedactedContents
}

prettyID := func(id int32) string { return fmt.Sprintf("ID: %d", id) }

// We're not diffing a file, so set an empty string for the URI argument.
edits := myers.ComputeEdits("", prevRedactedContents, r.siteConfig.RedactedContents)
diff := fmt.Sprint(gotextdiff.ToUnified(prettyID(prevID), prettyID(r.siteConfig.ID), prevRedactedContents, edits))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this require a call to fmt.Sprint or would string() be enough?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fmt.Sprint is required. The return type of gotextdiff.ToUnified does not have a String method but has a Format method: https://sourcegraph.com/github.com/hexops/gotextdiff/-/blob/unified.go?L165&subtree=true

Copy link
Copy Markdown
Contributor

@mrnugget mrnugget Feb 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it! (Might be a chance to open a tiny PR on hexops/gotextdiff and add the String() method that does exactly this: fmt.Sprint(u) 🙂 )

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great suggestion! PR filed: hexops/gotextdiff#3


// return cmp.Diff(r.siteConfig.Contents, r.previousSiteConfig.Contents)
return ""
return &diff
}

func (r SiteConfigurationChangeResolver) CreatedAt() gqlutil.DateTime {
Expand All @@ -59,3 +89,8 @@ func (r SiteConfigurationChangeResolver) CreatedAt() gqlutil.DateTime {
func (r SiteConfigurationChangeResolver) UpdatedAt() gqlutil.DateTime {
return gqlutil.DateTime{Time: r.siteConfig.UpdatedAt}
}

// One line wrapper to be able to use in tests as well.
func marshalSiteConfigurationChangeID(id int32) graphql.ID {
return relay.MarshalID(siteConfigurationChangeKind, &id)
}
Loading