From 114a6d511673f5ab757e6547788a2a980b7f5657 Mon Sep 17 00:00:00 2001 From: Tigor Hutasuhut Date: Fri, 9 Aug 2024 15:22:54 +0700 Subject: [PATCH] proto: added subreddits proto --- ...20240804155710_create_subreddits_table.sql | 4 +- .../20240808143100_create_images_table.sql | 20 ++++++- schemas/proto/subreddits/v1/create.proto | 32 +++++++++++ schemas/proto/subreddits/v1/delete.proto | 25 +++++++++ schemas/proto/subreddits/v1/get.proto | 42 +++++++++++++++ schemas/proto/subreddits/v1/list.proto | 19 +++++++ schemas/proto/subreddits/v1/resolve.proto | 17 ++++++ schemas/proto/subreddits/v1/subreddits.proto | 53 +++++++++++++++++++ schemas/proto/subreddits/v1/update.proto | 24 +++++++++ 9 files changed, 233 insertions(+), 3 deletions(-) create mode 100644 schemas/proto/subreddits/v1/create.proto create mode 100644 schemas/proto/subreddits/v1/delete.proto create mode 100644 schemas/proto/subreddits/v1/get.proto create mode 100644 schemas/proto/subreddits/v1/list.proto create mode 100644 schemas/proto/subreddits/v1/resolve.proto create mode 100644 schemas/proto/subreddits/v1/subreddits.proto create mode 100644 schemas/proto/subreddits/v1/update.proto diff --git a/schemas/migrations/20240804155710_create_subreddits_table.sql b/schemas/migrations/20240804155710_create_subreddits_table.sql index 1b0f311..94d366c 100644 --- a/schemas/migrations/20240804155710_create_subreddits_table.sql +++ b/schemas/migrations/20240804155710_create_subreddits_table.sql @@ -5,8 +5,8 @@ CREATE TABLE subreddits ( disable_scheduler TINYINT NOT NULL DEFAULT 0, "type" VARCHAR(5) NOT NULL DEFAULT 'r', schedule VARCHAR(20) NOT NULL DEFAULT '@daily', - countback INT NOT NULL DEFAULT 300, - image_cover VARCHAR(255) NOT NULL DEFAULT '', + countback BIGINT NOT NULL DEFAULT 300, + cover_image_id INTEGER, created_at BIGINT DEFAULT (strftime('%s', 'now')) NOT NULL, updated_at BIGINT DEFAULT (strftime('%s', 'now')) NOT NULL ); diff --git a/schemas/migrations/20240808143100_create_images_table.sql b/schemas/migrations/20240808143100_create_images_table.sql index feacd9e..1d927bb 100644 --- a/schemas/migrations/20240808143100_create_images_table.sql +++ b/schemas/migrations/20240808143100_create_images_table.sql @@ -27,7 +27,10 @@ CREATE TABLE images( CONSTRAINT fk_image_devices_slug FOREIGN KEY (device) REFERENCES devices(slug) - ON DELETE CASCADE + ON DELETE CASCADE, + CONSTRAINT fk_image_subreddit_cover + FOREIGN KEY (id) + REFERENCES subreddits(cover_image_id) ); CREATE INDEX idx_subreddit_images_blacklisted ON images(subreddit, blacklisted); @@ -36,6 +39,21 @@ CREATE INDEX idx_images_nsfw_blacklisted ON images(nsfw, blacklisted); CREATE INDEX idx_images_created_at_nsfw_blacklisted ON images(created_at DESC, nsfw, blacklisted); CREATE INDEX idx_images_blacklisted ON images(blacklisted); CREATE UNIQUE INDEX idx_unique_images_per_device ON images(device, post_name); + +CREATE TRIGGER images_update_timestamp AFTER UPDATE ON images FOR EACH ROW +BEGIN + UPDATE images SET updated_at = strftime('%s', 'now') WHERE id = old.id; +END; + +CREATE TRIGGER images_update_subreddit_cover_on_insert AFTER INSERT ON images FOR EACH ROW +BEGIN + UPDATE subreddits SET cover_image_id = new.id WHERE name = new.subreddit AND cover_image_id IS NULL; +END; + +CREATE TRIGGER images_update_subreddit_cover_on_delete AFTER DELETE ON images FOR EACH ROW +BEGIN + UPDATE subreddits SET cover_image_id = NULL WHERE cover_image_id = old.id; +END; -- +goose StatementEnd -- +goose Down diff --git a/schemas/proto/subreddits/v1/create.proto b/schemas/proto/subreddits/v1/create.proto new file mode 100644 index 0000000..e8cc75d --- /dev/null +++ b/schemas/proto/subreddits/v1/create.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; + +package subreddits.v1; + +import "buf/validate/validate.proto"; + +option go_package = "github.com/tigorlazuardi/bluemage/go/gen/proto/subreddits/v1"; + +message CreateSubredditRequest { + // name of the subreddits to insert. + // + // accept insensitive casing, but casing + // will change to what Reddit has for + // consistency. + string name = 1 [(buf.validate.field).string.min_len = 1]; + bool disable_scheduler = 2; + SubredditType type = 3; + // schedule is cron job spec to set schedule on when + // the runner for this subreddit runs. + string schedule = 4 [(buf.validate.field).string.min_len = 1]; + uint32 countback = 5 [(buf.validate.field).uint32.gt = 0]; +} + +enum SubredditType { + SUBREDDIT_TYPE_UNSPECIFIED = 0; + SUBREDDIT_TYPE_SUBREDDIT = 1; + SUBREDDIT_TYPE_USER = 2; +} + +message CreateSubredditResponse { + string name = 1; +} diff --git a/schemas/proto/subreddits/v1/delete.proto b/schemas/proto/subreddits/v1/delete.proto new file mode 100644 index 0000000..4ec2593 --- /dev/null +++ b/schemas/proto/subreddits/v1/delete.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; + +package subreddits.v1; + +import "buf/validate/validate.proto"; + +option go_package = "github.com/tigorlazuardi/bluemage/go/gen/proto/subreddits/v1"; + +message DeleteSubredditRequest { + // name of the subreddit to delete + string name = 1 [(buf.validate.field).string.min_len = 1]; + + // if true, delete all images associated with the subreddit + // from disk as well. + bool delete_images = 2; +} + +message DeleteSubredditResponse { + // name of the subreddit that was deleted. + string name = 1; + // number of images that were deleted. + // + // always 0 if delete_images is false. + uint32 deleted_images = 2; +} diff --git a/schemas/proto/subreddits/v1/get.proto b/schemas/proto/subreddits/v1/get.proto new file mode 100644 index 0000000..86fcae8 --- /dev/null +++ b/schemas/proto/subreddits/v1/get.proto @@ -0,0 +1,42 @@ +syntax = "proto3"; + +package subreddits.v1; + +import "buf/validate/validate.proto"; +import "subreddits/v1/create.proto"; + +option go_package = "github.com/tigorlazuardi/bluemage/go/gen/proto/subreddits/v1"; + +message GetSubredditRequest { + // name of the subreddit. Case insensitive. + // + // Returns error on not found. + string name = 1 [(buf.validate.field).string.min_len = 1]; +} + +message GetSubredditResponse { + string name = 1; + bool disable_scheduler = 2; + // type is never unspecified for this response. + // + // It's always either `SUBREDDIT_TYPE_SUBREDDIT` + // or `SUBREDDIT_TYPE_USER`. + SubredditType type = 3; + string schedule = 4; + uint32 countback = 5; + // cover_image_id is the id of an image that + // acts as the cover for this subreddit. + // + // May not be present in these kind of situations: + // + // - The background task runner has not run for this subreddit. + // - The image that is used as cover is deleted. + // - The subreddit does not contain any images. + // + // cover_image_id changes after every insert of + // new images in that subreddit, so avoid + // caching the image for too long. + optional uint32 cover_image_id = 6; + int64 created_at = 7; + int64 updated_at = 8; +} diff --git a/schemas/proto/subreddits/v1/list.proto b/schemas/proto/subreddits/v1/list.proto new file mode 100644 index 0000000..3fd25e6 --- /dev/null +++ b/schemas/proto/subreddits/v1/list.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package subreddits.v1; + +import "buf/validate/validate.proto"; +import "subreddits/v1/get.proto"; + +option go_package = "github.com/tigorlazuardi/bluemage/go/gen/proto/subreddits/v1"; + +message ListSubredditsRequest { + // name of the subreddit. Case insensitive. + // + // Returns error on not found. + string name = 1 [(buf.validate.field).string.min_len = 1]; +} + +message ListSubredditsResponse { + repeated GetSubredditResponse images = 1; +} diff --git a/schemas/proto/subreddits/v1/resolve.proto b/schemas/proto/subreddits/v1/resolve.proto new file mode 100644 index 0000000..4e7b832 --- /dev/null +++ b/schemas/proto/subreddits/v1/resolve.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +package subreddits.v1; + +import "buf/validate/validate.proto"; + +option go_package = "github.com/tigorlazuardi/bluemage/go/gen/proto/subreddits/v1"; + +message ResolveSubredditNameRequest { + // name of the subreddit to resolve (check existence and casing). + string name = 1 [(buf.validate.field).string.min_len = 1]; +} + +message ResolveSubredditNameResponse { + // resolved the actual subreddit name. + string resolved = 1; +} diff --git a/schemas/proto/subreddits/v1/subreddits.proto b/schemas/proto/subreddits/v1/subreddits.proto new file mode 100644 index 0000000..f2950ee --- /dev/null +++ b/schemas/proto/subreddits/v1/subreddits.proto @@ -0,0 +1,53 @@ +syntax = "proto3"; + +package subreddits.v1; + +import "subreddits/v1/create.proto"; +import "subreddits/v1/delete.proto"; +import "subreddits/v1/get.proto"; +import "subreddits/v1/list.proto"; +import "subreddits/v1/resolve.proto"; +import "subreddits/v1/update.proto"; + +option go_package = "github.com/tigorlazuardi/bluemage/go/gen/proto/subreddits/v1"; + +service SubredditsService { + // CreateSubreddit creates a new subreddit + // + // Returns the subreddit name. + // + // Returns the following error codes: + // - connect.CodeAlreadyExists if subreddit with the same name already exists. + // - connect.CodeInvalidArgument if validation failed, e.g. Invalid schedule cron format. + // - connect.CodeNotFound if the subreddit does not exist. + rpc CreateSubreddit(CreateSubredditRequest) returns (CreateSubredditResponse); + + // GetSubreddit returns a subreddit by its name. + // + // Returns error with connect.CodeNotFound if subreddit does not exist. + rpc GetSubreddit(GetSubredditRequest) returns (GetSubredditResponse); + + // ListSubreddits returns a list of subreddits. + rpc ListSubreddits(ListSubredditsRequest) returns (ListSubredditsResponse); + + // UpdateSubreddit updates a subreddit. + // + // Only the fields that are set in the request will be updated. + // + // Returns error with connect.CodeNotFound if subreddit does not exist. + rpc UpdateSubreddit(UpdateSubredditRequest) returns (UpdateSubredditResponse); + + // DeleteSubreddit deletes a subreddit. + // + // Returns error with connect.CodeNotFound if subreddit does not exist. + rpc DeleteSubreddit(DeleteSubredditRequest) returns (DeleteSubredditResponse); + + // ResolveSubredditName resolves the given subreddit name. + // + // The returned resolved_name is the name that is actually + // used in Reddit. + // + // Returns error with connect.CodeNotFound if subreddit does not exist. + // So this rpc endpoint also acts to check subreddit's existence. + rpc ResolveSubredditName(ResolveSubredditNameRequest) returns (ResolveSubredditNameResponse); +} diff --git a/schemas/proto/subreddits/v1/update.proto b/schemas/proto/subreddits/v1/update.proto new file mode 100644 index 0000000..f94a2a4 --- /dev/null +++ b/schemas/proto/subreddits/v1/update.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; + +package subreddits.v1; + +import "buf/validate/validate.proto"; + +option go_package = "github.com/tigorlazuardi/bluemage/go/gen/proto/subreddits/v1"; + +message UpdateSubredditRequest { + // The name of the subreddit to update. + string name = 1 [(buf.validate.field).string.min_len = 1]; + SubredditSetter set = 2; +} + +message SubredditSetter { + optional bool disable_scheduler = 1; + optional string schedule = 2 [(buf.validate.field).string.min_len = 1]; + optional int64 countback = 3 [(buf.validate.field).int64.gt = 0]; +} + +message UpdateSubredditResponse { + // The updated subreddit. + string name = 1; +}