Skip to content

Commit 2e050f3

Browse files
FeykoVilsol
andauthored
Add announcement API (#1)
* feat: Add announcement API (unfinished) * feat: Add announcement API (unfinished) * fix: finishing announcements + linting * fix(announcements): minor issues Co-authored-by: Vilsol <me@vil.so>
1 parent 41d8fc1 commit 2e050f3

File tree

11 files changed

+257
-1
lines changed

11 files changed

+257
-1
lines changed

auth/permissions.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ var (
4040
ID: "7",
4141
Description: "Allows user to bootstrap versions",
4242
}
43+
RoleEditAnnouncements = &Role{
44+
ID: "8",
45+
Description: "Allows user to manage announcements",
46+
}
4347
)
4448

4549
var (
@@ -54,6 +58,7 @@ var (
5458
RoleEditUsers,
5559
RoleEditSMLVersions,
5660
RoleEditBootstrapVersions,
61+
RoleEditAnnouncements,
5762
},
5863
}
5964
GroupModerator = &Group{
@@ -62,6 +67,7 @@ var (
6267
Roles: []*Role{
6368
RoleApproveMods,
6469
RoleApproveVersions,
70+
RoleEditAnnouncements,
6571
},
6672
}
6773
GroupSMLDev = &Group{

db/postgres/announcement.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package postgres
2+
3+
import (
4+
"context"
5+
6+
"github.com/patrickmn/go-cache"
7+
"github.com/satisfactorymodding/smr-api/util"
8+
)
9+
10+
func CreateAnnouncement(announcement *Announcement, ctx *context.Context) (*Announcement, error) {
11+
announcement.ID = util.GenerateUniqueID()
12+
DBCtx(ctx).Create(&announcement)
13+
return announcement, nil
14+
}
15+
16+
func GetAnnouncementByID(announcementID string, ctx *context.Context) *Announcement {
17+
cacheKey := "GetAnnouncementByID_" + announcementID
18+
19+
if announcement, ok := dbCache.Get(cacheKey); ok {
20+
return announcement.(*Announcement)
21+
}
22+
23+
var announcement Announcement
24+
DBCtx(ctx).Find(&announcement, "id = ?", announcementID)
25+
26+
if announcement.ID == "" {
27+
return nil
28+
}
29+
30+
dbCache.Set(cacheKey, announcement, cache.DefaultExpiration)
31+
32+
return &announcement
33+
}
34+
35+
func GetAnnouncements(ctx *context.Context) []Announcement {
36+
cacheKey := "GetAnnouncements"
37+
38+
if announcements, ok := dbCache.Get(cacheKey); ok {
39+
return announcements.([]Announcement)
40+
}
41+
42+
var announcements []Announcement
43+
DBCtx(ctx).Find(&announcements)
44+
45+
dbCache.Set(cacheKey, announcements, cache.DefaultExpiration)
46+
47+
return announcements
48+
}
49+
50+
func GetAnnouncementsByImportance(importance string, ctx *context.Context) []Announcement {
51+
cacheKey := "GetAnnouncementsByImportance_" + importance
52+
53+
if announcements, ok := dbCache.Get(cacheKey); ok {
54+
return announcements.([]Announcement)
55+
}
56+
57+
var announcements []Announcement
58+
DBCtx(ctx).Find(&announcements, "importance = ?", importance)
59+
60+
dbCache.Set(cacheKey, announcements, cache.DefaultExpiration)
61+
62+
return announcements
63+
}

db/postgres/postgres_types.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,10 @@ type BootstrapVersion struct {
148148
Link string
149149
Changelog string
150150
}
151+
152+
type Announcement struct {
153+
SMRModel
154+
155+
Message string
156+
Importance string
157+
}

docker-compose-dev.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,7 @@ services:
2222
entrypoint: sh
2323
command: -c 'mkdir -p /data/smr && /usr/bin/docker-entrypoint.sh minio server /data --console-address ":9001"'
2424
environment:
25+
MINIO_ROOT_USER: minio
26+
MINIO_ROOT_PASSWORD: minio123
2527
MINIO_ACCESS_KEY: REPLACE_ME_KEY
2628
MINIO_SECRET_KEY: REPLACE_ME_SECRET

gql/directive.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ func MakeDirective() generated.DirectiveRoot {
2727
CanEditUsers: canEditUsers,
2828
CanEditSMLVersions: canEditSMLVersions,
2929
CanEditBootstrapVersions: canEditBootstrapVersions,
30+
CanEditAnnouncements: canEditAnnouncements,
3031
}
3132
}
3233

@@ -212,3 +213,13 @@ func canEditBootstrapVersions(ctx context.Context, obj interface{}, next graphql
212213

213214
return nil, errors.New("user not authorized to perform this action")
214215
}
216+
217+
func canEditAnnouncements(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) {
218+
user := ctx.Value(postgres.UserKey{}).(*postgres.User)
219+
220+
if user.Has(auth.RoleEditAnnouncements, &ctx) {
221+
return next(ctx)
222+
}
223+
224+
return nil, errors.New("user not authorized to perform this action")
225+
}

gql/gql_types.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,23 @@ func DBVersionDependencyToGenerated(versionDependency *postgres.VersionDependenc
158158
Optional: versionDependency.Optional,
159159
}
160160
}
161+
162+
func DBAnnouncementToGenerated(announcement *postgres.Announcement) *generated.Announcement {
163+
if announcement == nil {
164+
return nil
165+
}
166+
167+
return &generated.Announcement{
168+
ID: announcement.ID,
169+
Message: announcement.Message,
170+
Importance: announcement.Importance,
171+
}
172+
}
173+
174+
func DBAnnouncementsToGeneratedSlice(announcements []postgres.Announcement) []*generated.Announcement {
175+
converted := make([]*generated.Announcement, len(announcements))
176+
for i, announcement := range announcements {
177+
converted[i] = DBAnnouncementToGenerated(&announcement)
178+
}
179+
return converted
180+
}

gql/resolver_announcements.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package gql
2+
3+
import (
4+
"context"
5+
6+
"github.com/pkg/errors"
7+
"github.com/satisfactorymodding/smr-api/util"
8+
"gopkg.in/go-playground/validator.v9"
9+
10+
"github.com/satisfactorymodding/smr-api/db/postgres"
11+
"github.com/satisfactorymodding/smr-api/generated"
12+
)
13+
14+
func (r *mutationResolver) CreateAnnouncement(ctx context.Context, announcement generated.NewAnnouncement) (*generated.Announcement, error) {
15+
wrapper, newCtx := WrapMutationTrace(ctx, "createAnnouncement")
16+
defer wrapper.end()
17+
18+
val := ctx.Value(util.ContextValidator{}).(*validator.Validate)
19+
if err := val.Struct(&announcement); err != nil {
20+
return nil, errors.Wrap(err, "validation failed")
21+
}
22+
23+
dbAnnouncement := &postgres.Announcement{
24+
Message: announcement.Message,
25+
Importance: announcement.Importance,
26+
}
27+
28+
resultAnnouncement, err := postgres.CreateAnnouncement(dbAnnouncement, &newCtx)
29+
if err != nil {
30+
return nil, err
31+
}
32+
return DBAnnouncementToGenerated(resultAnnouncement), nil
33+
}
34+
35+
func (r *mutationResolver) DeleteAnnouncement(ctx context.Context, announcementID string) (bool, error) {
36+
wrapper, newCtx := WrapMutationTrace(ctx, "deleteAnnouncement")
37+
defer wrapper.end()
38+
39+
dbAnnouncement := postgres.GetAnnouncementByID(announcementID, &newCtx)
40+
41+
if dbAnnouncement == nil {
42+
return false, errors.New("announcement not found")
43+
}
44+
45+
postgres.Delete(&dbAnnouncement, &newCtx)
46+
47+
return true, nil
48+
}
49+
50+
func (r *mutationResolver) UpdateAnnouncement(ctx context.Context, announcementID string, announcement generated.UpdateAnnouncement) (*generated.Announcement, error) {
51+
wrapper, newCtx := WrapMutationTrace(ctx, "updateAnnouncement")
52+
defer wrapper.end()
53+
54+
val := ctx.Value(util.ContextValidator{}).(*validator.Validate)
55+
if err := val.Struct(&announcement); err != nil {
56+
return nil, errors.Wrap(err, "validation failed")
57+
}
58+
59+
dbAnnouncement := postgres.GetAnnouncementByID(announcementID, &newCtx)
60+
61+
if dbAnnouncement == nil {
62+
return nil, errors.New("guide not found")
63+
}
64+
65+
SetStringINNOE(announcement.Message, &dbAnnouncement.Message)
66+
SetStringINNOE(announcement.Importance, &dbAnnouncement.Importance)
67+
68+
postgres.Save(&dbAnnouncement, &newCtx)
69+
70+
return DBAnnouncementToGenerated(dbAnnouncement), nil
71+
}
72+
73+
func (r *queryResolver) GetAnnouncement(ctx context.Context, announcementID string) (*generated.Announcement, error) {
74+
wrapper, newCtx := WrapQueryTrace(ctx, "getAnnouncement")
75+
defer wrapper.end()
76+
77+
announcement := postgres.GetAnnouncementByID(announcementID, &newCtx)
78+
79+
return DBAnnouncementToGenerated(announcement), nil
80+
}
81+
82+
func (r *queryResolver) GetAnnouncements(ctx context.Context) ([]*generated.Announcement, error) {
83+
wrapper, newCtx := WrapQueryTrace(ctx, "getAnnouncements")
84+
defer wrapper.end()
85+
86+
announcements := postgres.GetAnnouncements(&newCtx)
87+
88+
return DBAnnouncementsToGeneratedSlice(announcements), nil
89+
}
90+
91+
func (r *queryResolver) GetAnnouncementsByImportance(ctx context.Context, importance string) ([]*generated.Announcement, error) {
92+
wrapper, newCtx := WrapQueryTrace(ctx, "getAnnouncementsByImportance")
93+
defer wrapper.end()
94+
95+
announcements := postgres.GetAnnouncementsByImportance(importance, &newCtx)
96+
97+
return DBAnnouncementsToGeneratedSlice(announcements), nil
98+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
drop table if exists announcements
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
create table if not exists announcements
2+
(
3+
id varchar(14) not null
4+
constraint announcements_pkey primary key,
5+
message text not null,
6+
importance text not null,
7+
created_at timestamp with time zone,
8+
updated_at timestamp with time zone,
9+
deleted_at timestamp with time zone
10+
)

schemas/announcements.graphql

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
### Types
2+
3+
scalar AnnouncementID
4+
5+
type Announcement {
6+
id: AnnouncementID!
7+
message: String!
8+
importance: String!
9+
}
10+
11+
### Inputs
12+
13+
input NewAnnouncement {
14+
message: String!
15+
importance: String!
16+
}
17+
18+
input UpdateAnnouncement {
19+
message: String
20+
importance: String
21+
}
22+
23+
### Queries
24+
25+
extend type Query {
26+
getAnnouncement(announcementId: AnnouncementID!): Announcement
27+
getAnnouncements: [Announcement!]!
28+
getAnnouncementsByImportance(importance: String!): [Announcement!]!
29+
}
30+
31+
### Mutations
32+
33+
extend type Mutation {
34+
createAnnouncement(announcement: NewAnnouncement!): Announcement @canEditAnnouncements @isLoggedIn
35+
updateAnnouncement(announcementId: AnnouncementID!, announcement: UpdateAnnouncement!): Announcement! @canEditAnnouncements @isLoggedIn
36+
deleteAnnouncement(announcementId: AnnouncementID!): Boolean! @canEditAnnouncements @isLoggedIn
37+
}

0 commit comments

Comments
 (0)