From 71477f71f6f716cabc3becc334325508a4aead2b Mon Sep 17 00:00:00 2001 From: Lukas Schulte Pelkum Date: Mon, 19 Jul 2021 15:11:52 +0200 Subject: [PATCH] Implement paste metadata --- internal/shared/paste.go | 13 +++++----- .../migrations/000003_add_metadata.down.sql | 5 ++++ .../migrations/000003_add_metadata.up.sql | 5 ++++ internal/storage/postgres/postgres_driver.go | 7 ++--- internal/web/controllers/v1/paste_adapter.go | 26 +++++++++++++++++++ internal/web/controllers/v1/pastes.go | 9 +++---- internal/web/controllers/v2/pastes.go | 23 ++++++++++++++-- 7 files changed, 72 insertions(+), 16 deletions(-) create mode 100644 internal/storage/postgres/migrations/000003_add_metadata.down.sql create mode 100644 internal/storage/postgres/migrations/000003_add_metadata.up.sql create mode 100644 internal/web/controllers/v1/paste_adapter.go diff --git a/internal/shared/paste.go b/internal/shared/paste.go index ad7b176..06e17b9 100644 --- a/internal/shared/paste.go +++ b/internal/shared/paste.go @@ -8,12 +8,13 @@ import ( // Paste represents a saved paste type Paste struct { - ID string `json:"id" bson:"_id"` - Content string `json:"content" bson:"content"` - DeletionToken string `json:"deletionToken,omitempty" bson:"deletionToken"` // Required for legacy paste storage support - ModificationToken string `json:"modificationToken,omitempty" bson:"modificationToken"` - Created int64 `json:"created" bson:"created"` - AutoDelete bool `json:"autoDelete" bson:"autoDelete"` + ID string `json:"id" bson:"_id"` + Content string `json:"content" bson:"content"` + DeletionToken string `json:"deletionToken,omitempty" bson:"deletionToken"` // Required for legacy paste storage support + ModificationToken string `json:"modificationToken,omitempty" bson:"modificationToken"` + Created int64 `json:"created" bson:"created"` + AutoDelete bool `json:"autoDelete" bson:"autoDelete"` + Metadata map[string]interface{} `json:"metadata" bson:"metadata"` } // HashModificationToken hashes the current modification token of a paste diff --git a/internal/storage/postgres/migrations/000003_add_metadata.down.sql b/internal/storage/postgres/migrations/000003_add_metadata.down.sql new file mode 100644 index 0000000..a9915b0 --- /dev/null +++ b/internal/storage/postgres/migrations/000003_add_metadata.down.sql @@ -0,0 +1,5 @@ +begin; + +alter table if exists "pastes" drop column "metadata"; + +commit; \ No newline at end of file diff --git a/internal/storage/postgres/migrations/000003_add_metadata.up.sql b/internal/storage/postgres/migrations/000003_add_metadata.up.sql new file mode 100644 index 0000000..be9090a --- /dev/null +++ b/internal/storage/postgres/migrations/000003_add_metadata.up.sql @@ -0,0 +1,5 @@ +begin; + +alter table if exists "pastes" add column "metadata" jsonb not null; + +commit; \ No newline at end of file diff --git a/internal/storage/postgres/postgres_driver.go b/internal/storage/postgres/postgres_driver.go index 859b1ad..b2f28a5 100644 --- a/internal/storage/postgres/postgres_driver.go +++ b/internal/storage/postgres/postgres_driver.go @@ -82,7 +82,7 @@ func (driver *PostgresDriver) Get(id string) (*shared.Paste, error) { row := driver.pool.QueryRow(context.Background(), query, id) paste := new(shared.Paste) - if err := row.Scan(&paste.ID, &paste.Content, &paste.ModificationToken, &paste.Created, &paste.AutoDelete); err != nil { + if err := row.Scan(&paste.ID, &paste.Content, &paste.ModificationToken, &paste.Created, &paste.AutoDelete, &paste.Metadata); err != nil { if errors.Is(err, pgx.ErrNoRows) { return nil, nil } @@ -100,10 +100,11 @@ func (driver *PostgresDriver) Save(paste *shared.Paste) error { SET content = excluded.token, modificationToken = excluded.modificationToken, created = excluded.created, - autoDelete = excluded.autoDelete + autoDelete = excluded.autoDelete, + metadata = excluded.metadata ` - _, err := driver.pool.Exec(context.Background(), query, paste.ID, paste.Content, paste.ModificationToken, paste.Created, paste.AutoDelete) + _, err := driver.pool.Exec(context.Background(), query, paste.ID, paste.Content, paste.ModificationToken, paste.Created, paste.AutoDelete, paste.Metadata) return err } diff --git a/internal/web/controllers/v1/paste_adapter.go b/internal/web/controllers/v1/paste_adapter.go new file mode 100644 index 0000000..a977b1d --- /dev/null +++ b/internal/web/controllers/v1/paste_adapter.go @@ -0,0 +1,26 @@ +package v1 + +import "github.com/lus/pasty/internal/shared" + +type legacyPaste struct { + ID string `json:"id"` + Content string `json:"content"` + DeletionToken string `json:"deletionToken,omitempty"` + Created int64 `json:"created"` + AutoDelete bool `json:"autoDelete"` +} + +func legacyFromModern(paste *shared.Paste) *legacyPaste { + deletionToken := paste.ModificationToken + if deletionToken == "" { + deletionToken = paste.DeletionToken + } + + return &legacyPaste{ + ID: paste.ID, + Content: paste.Content, + DeletionToken: deletionToken, + Created: paste.Created, + AutoDelete: paste.AutoDelete, + } +} diff --git a/internal/web/controllers/v1/pastes.go b/internal/web/controllers/v1/pastes.go index 0277679..3e617ca 100644 --- a/internal/web/controllers/v1/pastes.go +++ b/internal/web/controllers/v1/pastes.go @@ -37,11 +37,11 @@ func v1GetPaste(ctx *fasthttp.RequestCtx) { ctx.SetBodyString("paste not found") return } - paste.DeletionToken = "" - paste.ModificationToken = "" + legacyPaste := legacyFromModern(paste) + legacyPaste.DeletionToken = "" // Respond with the paste - jsonData, err := json.Marshal(paste) + jsonData, err := json.Marshal(legacyPaste) if err != nil { ctx.SetStatusCode(fasthttp.StatusInternalServerError) ctx.SetBodyString(err.Error()) @@ -115,9 +115,8 @@ func v1PostPaste(ctx *fasthttp.RequestCtx) { } // Respond with the paste - pasteCopy := *paste + pasteCopy := legacyFromModern(paste) pasteCopy.DeletionToken = modificationToken - pasteCopy.ModificationToken = "" jsonData, err := json.Marshal(pasteCopy) if err != nil { ctx.SetStatusCode(fasthttp.StatusInternalServerError) diff --git a/internal/web/controllers/v2/pastes.go b/internal/web/controllers/v2/pastes.go index 096da51..04d153c 100644 --- a/internal/web/controllers/v2/pastes.go +++ b/internal/web/controllers/v2/pastes.go @@ -40,6 +40,10 @@ func middlewareInjectPaste(next fasthttp.RequestHandler) fasthttp.RequestHandler return } + if paste.Metadata == nil { + paste.Metadata = map[string]interface{}{} + } + ctx.SetUserValue("_paste", paste) next(ctx) @@ -90,7 +94,8 @@ func endpointGetPaste(ctx *fasthttp.RequestCtx) { } type endpointCreatePastePayload struct { - Content string `json:"content"` + Content string `json:"content"` + Metadata map[string]interface{} `json:"metadata"` } // endpointCreatePaste handles the 'POST /v2/pastes' endpoint @@ -122,11 +127,15 @@ func endpointCreatePaste(ctx *fasthttp.RequestCtx) { } // Prepare the paste object + if payload.Metadata == nil { + payload.Metadata = map[string]interface{}{} + } paste := &shared.Paste{ ID: id, Content: payload.Content, Created: time.Now().Unix(), AutoDelete: config.Current.AutoDelete.Enabled, + Metadata: payload.Metadata, } // Create a new modification token if enabled @@ -165,7 +174,8 @@ func endpointCreatePaste(ctx *fasthttp.RequestCtx) { } type endpointModifyPastePayload struct { - Content *string `json:"content"` + Content *string `json:"content"` + Metadata map[string]interface{} `json:"metadata"` } // endpointModifyPaste handles the 'PATCH /v2/pastes/{id}' endpoint @@ -193,6 +203,15 @@ func endpointModifyPaste(ctx *fasthttp.RequestCtx) { if payload.Content != nil { paste.Content = *payload.Content } + if payload.Metadata != nil { + for key, value := range payload.Metadata { + if value == nil { + delete(paste.Metadata, key) + continue + } + paste.Metadata[key] = value + } + } // Save the modified paste if err := storage.Current.Save(paste); err != nil {