Skip to content
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
6 changes: 6 additions & 0 deletions auth/permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ var (
ID: "7",
Description: "Allows user to bootstrap versions",
}
RoleEditAnnouncements = &Role{
ID: "8",
Description: "Allows user to manage announcements",
}
)

var (
Expand All @@ -54,6 +58,7 @@ var (
RoleEditUsers,
RoleEditSMLVersions,
RoleEditBootstrapVersions,
RoleEditAnnouncements,
},
}
GroupModerator = &Group{
Expand All @@ -62,6 +67,7 @@ var (
Roles: []*Role{
RoleApproveMods,
RoleApproveVersions,
RoleEditAnnouncements,
},
}
GroupSMLDev = &Group{
Expand Down
63 changes: 63 additions & 0 deletions db/postgres/announcement.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package postgres

import (
"context"

"github.com/patrickmn/go-cache"
"github.com/satisfactorymodding/smr-api/util"
)

func CreateAnnouncement(announcement *Announcement, ctx *context.Context) (*Announcement, error) {
announcement.ID = util.GenerateUniqueID()
DBCtx(ctx).Create(&announcement)
return announcement, nil
}

func GetAnnouncementByID(announcementID string, ctx *context.Context) *Announcement {
cacheKey := "GetAnnouncementByID_" + announcementID

if announcement, ok := dbCache.Get(cacheKey); ok {
return announcement.(*Announcement)
}

var announcement Announcement
DBCtx(ctx).Find(&announcement, "id = ?", announcementID)

if announcement.ID == "" {
return nil
}

dbCache.Set(cacheKey, announcement, cache.DefaultExpiration)

return &announcement
}

func GetAnnouncements(ctx *context.Context) []Announcement {
cacheKey := "GetAnnouncements"

if announcements, ok := dbCache.Get(cacheKey); ok {
return announcements.([]Announcement)
}

var announcements []Announcement
DBCtx(ctx).Find(&announcements)

dbCache.Set(cacheKey, announcements, cache.DefaultExpiration)

return announcements
}

func GetAnnouncementsByImportance(importance string, ctx *context.Context) []Announcement {
cacheKey := "GetAnnouncementsByImportance_" + importance

if announcements, ok := dbCache.Get(cacheKey); ok {
return announcements.([]Announcement)
}

var announcements []Announcement
DBCtx(ctx).Find(&announcements, "importance = ?", importance)

dbCache.Set(cacheKey, announcements, cache.DefaultExpiration)

return announcements
}
7 changes: 7 additions & 0 deletions db/postgres/postgres_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,10 @@ type BootstrapVersion struct {
Link string
Changelog string
}

type Announcement struct {
SMRModel

Message string
Importance string
}
2 changes: 2 additions & 0 deletions docker-compose-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,7 @@ services:
entrypoint: sh
command: -c 'mkdir -p /data/smr && /usr/bin/docker-entrypoint.sh minio server /data --console-address ":9001"'
environment:
MINIO_ROOT_USER: minio
MINIO_ROOT_PASSWORD: minio123
MINIO_ACCESS_KEY: REPLACE_ME_KEY
MINIO_SECRET_KEY: REPLACE_ME_SECRET
11 changes: 11 additions & 0 deletions gql/directive.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func MakeDirective() generated.DirectiveRoot {
CanEditUsers: canEditUsers,
CanEditSMLVersions: canEditSMLVersions,
CanEditBootstrapVersions: canEditBootstrapVersions,
CanEditAnnouncements: canEditAnnouncements,
}
}

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

return nil, errors.New("user not authorized to perform this action")
}

func canEditAnnouncements(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) {
user := ctx.Value(postgres.UserKey{}).(*postgres.User)

if user.Has(auth.RoleEditAnnouncements, &ctx) {
return next(ctx)
}

return nil, errors.New("user not authorized to perform this action")
}
20 changes: 20 additions & 0 deletions gql/gql_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,23 @@ func DBVersionDependencyToGenerated(versionDependency *postgres.VersionDependenc
Optional: versionDependency.Optional,
}
}

func DBAnnouncementToGenerated(announcement *postgres.Announcement) *generated.Announcement {
if announcement == nil {
return nil
}

return &generated.Announcement{
ID: announcement.ID,
Message: announcement.Message,
Importance: announcement.Importance,
}
}

func DBAnnouncementsToGeneratedSlice(announcements []postgres.Announcement) []*generated.Announcement {
converted := make([]*generated.Announcement, len(announcements))
for i, announcement := range announcements {
converted[i] = DBAnnouncementToGenerated(&announcement)
}
return converted
}
98 changes: 98 additions & 0 deletions gql/resolver_announcements.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package gql

import (
"context"

"github.com/pkg/errors"
"github.com/satisfactorymodding/smr-api/util"
"gopkg.in/go-playground/validator.v9"

"github.com/satisfactorymodding/smr-api/db/postgres"
"github.com/satisfactorymodding/smr-api/generated"
)

func (r *mutationResolver) CreateAnnouncement(ctx context.Context, announcement generated.NewAnnouncement) (*generated.Announcement, error) {
wrapper, newCtx := WrapMutationTrace(ctx, "createAnnouncement")
defer wrapper.end()

val := ctx.Value(util.ContextValidator{}).(*validator.Validate)
if err := val.Struct(&announcement); err != nil {
return nil, errors.Wrap(err, "validation failed")
}

dbAnnouncement := &postgres.Announcement{
Message: announcement.Message,
Importance: announcement.Importance,
}

resultAnnouncement, err := postgres.CreateAnnouncement(dbAnnouncement, &newCtx)
if err != nil {
return nil, err
}
return DBAnnouncementToGenerated(resultAnnouncement), nil
}

func (r *mutationResolver) DeleteAnnouncement(ctx context.Context, announcementID string) (bool, error) {
wrapper, newCtx := WrapMutationTrace(ctx, "deleteAnnouncement")
defer wrapper.end()

dbAnnouncement := postgres.GetAnnouncementByID(announcementID, &newCtx)

if dbAnnouncement == nil {
return false, errors.New("announcement not found")
}

postgres.Delete(&dbAnnouncement, &newCtx)

return true, nil
}

func (r *mutationResolver) UpdateAnnouncement(ctx context.Context, announcementID string, announcement generated.UpdateAnnouncement) (*generated.Announcement, error) {
wrapper, newCtx := WrapMutationTrace(ctx, "updateAnnouncement")
defer wrapper.end()

val := ctx.Value(util.ContextValidator{}).(*validator.Validate)
if err := val.Struct(&announcement); err != nil {
return nil, errors.Wrap(err, "validation failed")
}

dbAnnouncement := postgres.GetAnnouncementByID(announcementID, &newCtx)

if dbAnnouncement == nil {
return nil, errors.New("guide not found")
}

SetStringINNOE(announcement.Message, &dbAnnouncement.Message)
SetStringINNOE(announcement.Importance, &dbAnnouncement.Importance)

postgres.Save(&dbAnnouncement, &newCtx)

return DBAnnouncementToGenerated(dbAnnouncement), nil
}

func (r *queryResolver) GetAnnouncement(ctx context.Context, announcementID string) (*generated.Announcement, error) {
wrapper, newCtx := WrapQueryTrace(ctx, "getAnnouncement")
defer wrapper.end()

announcement := postgres.GetAnnouncementByID(announcementID, &newCtx)

return DBAnnouncementToGenerated(announcement), nil
}

func (r *queryResolver) GetAnnouncements(ctx context.Context) ([]*generated.Announcement, error) {
wrapper, newCtx := WrapQueryTrace(ctx, "getAnnouncements")
defer wrapper.end()

announcements := postgres.GetAnnouncements(&newCtx)

return DBAnnouncementsToGeneratedSlice(announcements), nil
}

func (r *queryResolver) GetAnnouncementsByImportance(ctx context.Context, importance string) ([]*generated.Announcement, error) {
wrapper, newCtx := WrapQueryTrace(ctx, "getAnnouncementsByImportance")
defer wrapper.end()

announcements := postgres.GetAnnouncementsByImportance(importance, &newCtx)

return DBAnnouncementsToGeneratedSlice(announcements), nil
}
1 change: 1 addition & 0 deletions migrations/sql/000018_add_announcements.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
drop table if exists announcements
10 changes: 10 additions & 0 deletions migrations/sql/000018_add_announcements.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
create table if not exists announcements
(
id varchar(14) not null
constraint announcements_pkey primary key,
message text not null,
importance text not null,
created_at timestamp with time zone,
updated_at timestamp with time zone,
deleted_at timestamp with time zone
)
37 changes: 37 additions & 0 deletions schemas/announcements.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
### Types

scalar AnnouncementID

type Announcement {
id: AnnouncementID!
message: String!
importance: String!
}

### Inputs

input NewAnnouncement {
message: String!
importance: String!
}

input UpdateAnnouncement {
message: String
importance: String
}

### Queries

extend type Query {
getAnnouncement(announcementId: AnnouncementID!): Announcement
getAnnouncements: [Announcement!]!
getAnnouncementsByImportance(importance: String!): [Announcement!]!
}

### Mutations

extend type Mutation {
createAnnouncement(announcement: NewAnnouncement!): Announcement @canEditAnnouncements @isLoggedIn
updateAnnouncement(announcementId: AnnouncementID!, announcement: UpdateAnnouncement!): Announcement! @canEditAnnouncements @isLoggedIn
deleteAnnouncement(announcementId: AnnouncementID!): Boolean! @canEditAnnouncements @isLoggedIn
}
3 changes: 2 additions & 1 deletion schemas/directives.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ directive @canApproveMods on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
directive @canApproveVersions on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
directive @canEditUsers on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
directive @canEditSMLVersions on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
directive @canEditBootstrapVersions on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
directive @canEditBootstrapVersions on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
directive @canEditAnnouncements on FIELD_DEFINITION | INPUT_FIELD_DEFINITION