Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ce38d61b1 | ||
|
|
dc3f77f8da | ||
|
|
6321ea1893 | ||
|
|
9b346fb97b | ||
|
|
ed21b7c8cd |
+34
-16
@@ -1,21 +1,39 @@
|
|||||||
workspace:
|
name: rpdata-api
|
||||||
base: /go
|
|
||||||
path: src/git.aiterp.net/rpdata/api
|
|
||||||
|
|
||||||
pipeline:
|
kind: pipeline
|
||||||
docker-release:
|
type: docker
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: go-test
|
||||||
|
image: golang:1.14
|
||||||
|
commands:
|
||||||
|
- go generate ./...
|
||||||
|
- go test -v ./...
|
||||||
|
|
||||||
|
- name: docker-release-tag
|
||||||
image: plugins/docker
|
image: plugins/docker
|
||||||
repo: vmaple/rpdata
|
settings:
|
||||||
auto_tag: true
|
auto_tag: true
|
||||||
secrets: [ docker_username, docker_password ]
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
repo: r.vmaple.dev/rpdata/rpdata-api
|
||||||
|
registry: r.vmaple.dev
|
||||||
when:
|
when:
|
||||||
event: tag
|
event:
|
||||||
ref: refs/tags/*
|
- tag
|
||||||
docker-release-latest:
|
|
||||||
|
- name: docker-release-latest
|
||||||
image: plugins/docker
|
image: plugins/docker
|
||||||
repo: vmaple/rpdata
|
settings:
|
||||||
tags: latest
|
auto_tag: false
|
||||||
secrets: [ docker_username, docker_password ]
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
repo: r.vmaple.dev/rpdata/rpdata-api
|
||||||
|
registry: r.vmaple.dev
|
||||||
when:
|
when:
|
||||||
event: tag
|
event:
|
||||||
ref: refs/tags/*
|
- tag
|
||||||
@@ -10,6 +10,9 @@ rpdata-*
|
|||||||
.vscode
|
.vscode
|
||||||
.idea
|
.idea
|
||||||
|
|
||||||
|
/main
|
||||||
|
/main.exe
|
||||||
|
|
||||||
/graph2/graphcore/*_gen.go
|
/graph2/graphcore/*_gen.go
|
||||||
generated.gql
|
generated.gql
|
||||||
generated.go
|
generated.go
|
||||||
|
|||||||
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
## 1. Build Server
|
## 1. Build Server
|
||||||
# Use Go 1.12
|
# Use Go 1.14
|
||||||
FROM golang:1.12 AS build-server
|
FROM golang:1.14 AS build-server
|
||||||
|
|
||||||
# Load repository into docker
|
# Load repository into docker
|
||||||
WORKDIR /project/
|
WORKDIR /project/
|
||||||
@@ -15,7 +15,7 @@ RUN go build -installsuffix cgo -ldflags="-s -w" -o /binaries/rpdata-server ./cm
|
|||||||
|
|
||||||
## 2. Distribute
|
## 2. Distribute
|
||||||
# Use alpine linux
|
# Use alpine linux
|
||||||
FROM alpine:3.7
|
FROM alpine:3.12
|
||||||
|
|
||||||
# Install certificates to enable HTTPS GET requests, and tzdata for log imports.
|
# Install certificates to enable HTTPS GET requests, and tzdata for log imports.
|
||||||
RUN apk add --no-cache ca-certificates tzdata
|
RUN apk add --no-cache ca-certificates tzdata
|
||||||
|
|||||||
+28
-12
@@ -145,26 +145,33 @@ func (s *LogService) Import(ctx context.Context, importer models.LogImporter, da
|
|||||||
parsed.Log.EventName = eventName
|
parsed.Log.EventName = eventName
|
||||||
parsed.Log.ChannelName = channelName
|
parsed.Log.ChannelName = channelName
|
||||||
|
|
||||||
log, err := s.logs.Insert(ctx, parsed.Log)
|
newLog, err := s.logs.Insert(ctx, parsed.Log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, post := range parsed.Posts {
|
for _, post := range parsed.Posts {
|
||||||
post.LogID = log.ShortID
|
post.LogID = newLog.ShortID
|
||||||
}
|
}
|
||||||
|
|
||||||
posts, err := s.posts.InsertMany(ctx, parsed.Posts...)
|
posts, err := s.posts.InsertMany(ctx, parsed.Posts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = s.logs.Delete(ctx, *log)
|
_ = s.logs.Delete(ctx, *newLog)
|
||||||
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.changeService.Submit(ctx, models.ChangeModelLog, "add", true, changekeys.Listed(log), log)
|
s.changeService.Submit(ctx, models.ChangeModelLog, "add", true, changekeys.Listed(newLog), newLog)
|
||||||
s.changeService.Submit(ctx, models.ChangeModelPost, "add", true, changekeys.Listed(log, posts), log, posts)
|
s.changeService.Submit(ctx, models.ChangeModelPost, "add", true, changekeys.Listed(newLog, posts), newLog, posts)
|
||||||
|
|
||||||
results = append(results, log)
|
refreshedLog, err := s.refreshLogCharacters(ctx, *newLog, nil, false)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to update characters in newLog %s: %s", newLog.ID, err)
|
||||||
|
} else {
|
||||||
|
newLog = refreshedLog
|
||||||
|
}
|
||||||
|
|
||||||
|
results = append(results, newLog)
|
||||||
}
|
}
|
||||||
case models.LogImporterForumLog:
|
case models.LogImporterForumLog:
|
||||||
{
|
{
|
||||||
@@ -174,7 +181,7 @@ func (s *LogService) Import(ctx context.Context, importer models.LogImporter, da
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, parsed := range parseResults {
|
for _, parsed := range parseResults {
|
||||||
log, err := s.logs.Insert(ctx, parsed.Log)
|
newLog, err := s.logs.Insert(ctx, parsed.Log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -182,18 +189,27 @@ func (s *LogService) Import(ctx context.Context, importer models.LogImporter, da
|
|||||||
parsed.Log.ChannelName = channelName
|
parsed.Log.ChannelName = channelName
|
||||||
|
|
||||||
for _, post := range parsed.Posts {
|
for _, post := range parsed.Posts {
|
||||||
post.LogID = log.ShortID
|
post.LogID = newLog.ShortID
|
||||||
}
|
}
|
||||||
|
|
||||||
posts, err := s.posts.InsertMany(ctx, parsed.Posts...)
|
posts, err := s.posts.InsertMany(ctx, parsed.Posts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = s.logs.Delete(ctx, *log)
|
_ = s.logs.Delete(ctx, *newLog)
|
||||||
|
|
||||||
return nil, err
|
return results, err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.changeService.Submit(ctx, models.ChangeModelLog, "add", true, changekeys.Listed(log), log)
|
refreshedLog, err := s.refreshLogCharacters(ctx, *newLog, nil, false)
|
||||||
s.changeService.Submit(ctx, models.ChangeModelPost, "add", true, changekeys.Listed(log, posts), log, posts)
|
if err != nil {
|
||||||
|
log.Printf("Failed to update characters in newLog %s: %s", newLog.ID, err)
|
||||||
|
} else {
|
||||||
|
newLog = refreshedLog
|
||||||
|
}
|
||||||
|
|
||||||
|
s.changeService.Submit(ctx, models.ChangeModelLog, "add", true, changekeys.Listed(newLog), newLog)
|
||||||
|
s.changeService.Submit(ctx, models.ChangeModelPost, "add", true, changekeys.Listed(newLog, posts), newLog, posts)
|
||||||
|
|
||||||
|
results = append(results, newLog)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -2,14 +2,26 @@ package parsers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"git.aiterp.net/rpdata/api/models"
|
"git.aiterp.net/rpdata/api/models"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrNotPost is returned by parsePost if the line is empty or not a post.
|
type ParseError struct {
|
||||||
var ErrNotPost = errors.New("not a post")
|
Line string
|
||||||
|
Problem string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ParseError) Error() string {
|
||||||
|
return fmt.Sprintf("Unrecognized post: %s (error: %s)", e.Line, e.Problem)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsParseError(err error) bool {
|
||||||
|
_, ok := err.(*ParseError)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
// ErrEmptyLog is returned by ParseLog if there are no (valid) posts in the log.
|
// ErrEmptyLog is returned by ParseLog if there are no (valid) posts in the log.
|
||||||
var ErrEmptyLog = errors.New("no valid posts found in log")
|
var ErrEmptyLog = errors.New("no valid posts found in log")
|
||||||
@@ -56,24 +68,36 @@ func MircPost(line string, date time.Time, prev models.Post) (models.Post, error
|
|||||||
// Do basic validation
|
// Do basic validation
|
||||||
line = strings.Trim(line, " \t\n\r")
|
line = strings.Trim(line, " \t\n\r")
|
||||||
if len(line) == 0 || !strings.HasPrefix(line, "[") {
|
if len(line) == 0 || !strings.HasPrefix(line, "[") {
|
||||||
return models.Post{}, ErrNotPost
|
return models.Post{}, &ParseError{
|
||||||
|
Line: line,
|
||||||
|
Problem: "no timestamp",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse timestamp
|
// Parse timestamp
|
||||||
tsEndIndex := strings.IndexByte(line, ']')
|
tsEndIndex := strings.IndexByte(line, ']')
|
||||||
if tsEndIndex == -1 || len(line) < tsEndIndex+5 {
|
if tsEndIndex == -1 || len(line) < tsEndIndex+5 {
|
||||||
return models.Post{}, ErrNotPost
|
return models.Post{}, &ParseError{
|
||||||
|
Line: line,
|
||||||
|
Problem: "incomplete timestamp",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
tsStr := line[1:tsEndIndex]
|
tsStr := line[1:tsEndIndex]
|
||||||
tsSplit := strings.Split(tsStr, ":")
|
tsSplit := strings.Split(tsStr, ":")
|
||||||
tsUnits := make([]int, len(tsSplit))
|
tsUnits := make([]int, len(tsSplit))
|
||||||
if len(tsSplit) < 2 {
|
if len(tsSplit) < 2 {
|
||||||
return models.Post{}, ErrNotPost
|
return models.Post{}, &ParseError{
|
||||||
|
Line: line,
|
||||||
|
Problem: "invalid timestamp",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for i := range tsSplit {
|
for i := range tsSplit {
|
||||||
n, err := strconv.Atoi(tsSplit[i])
|
n, err := strconv.Atoi(tsSplit[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.Post{}, ErrNotPost
|
return models.Post{}, &ParseError{
|
||||||
|
Line: line,
|
||||||
|
Problem: "invalid number in timestamp",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tsUnits[i] = n
|
tsUnits[i] = n
|
||||||
@@ -94,7 +118,10 @@ func MircPost(line string, date time.Time, prev models.Post) (models.Post, error
|
|||||||
if line[tsEndIndex+2] == '*' {
|
if line[tsEndIndex+2] == '*' {
|
||||||
split := strings.SplitN(line[tsEndIndex+4:], " ", 2)
|
split := strings.SplitN(line[tsEndIndex+4:], " ", 2)
|
||||||
if len(split) == 1 {
|
if len(split) == 1 {
|
||||||
return models.Post{}, ErrNotPost
|
return models.Post{}, &ParseError{
|
||||||
|
Line: line,
|
||||||
|
Problem: "post is empty",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
post := models.Post{
|
post := models.Post{
|
||||||
@@ -115,7 +142,10 @@ func MircPost(line string, date time.Time, prev models.Post) (models.Post, error
|
|||||||
} else if line[tsEndIndex+2] == '<' {
|
} else if line[tsEndIndex+2] == '<' {
|
||||||
split := strings.SplitN(line[tsEndIndex+2:], " ", 2)
|
split := strings.SplitN(line[tsEndIndex+2:], " ", 2)
|
||||||
if len(split) == 1 {
|
if len(split) == 1 {
|
||||||
return models.Post{}, ErrNotPost
|
return models.Post{}, &ParseError{
|
||||||
|
Line: line,
|
||||||
|
Problem: "post is empty",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
post := models.Post{
|
post := models.Post{
|
||||||
@@ -134,6 +164,9 @@ func MircPost(line string, date time.Time, prev models.Post) (models.Post, error
|
|||||||
|
|
||||||
return post, nil
|
return post, nil
|
||||||
} else {
|
} else {
|
||||||
return models.Post{}, ErrNotPost
|
return models.Post{}, &ParseError{
|
||||||
|
Line: line,
|
||||||
|
Problem: "line is neither action nor text post",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,29 +58,38 @@ func TestMircPost(t *testing.T) {
|
|||||||
func TestMircPostErrors(t *testing.T) {
|
func TestMircPostErrors(t *testing.T) {
|
||||||
table := []struct {
|
table := []struct {
|
||||||
Input string
|
Input string
|
||||||
Err error
|
Err string
|
||||||
}{
|
}{
|
||||||
{"[12:34] <Stuff> Things said.", nil},
|
{"[12:34] <Stuff> Things said.", ""},
|
||||||
{"[12:34] >Stuff> Things said.", parsers.ErrNotPost},
|
{"[12:34] >Stuff> Things said.", "line is neither action nor text post"},
|
||||||
{"12:34] <Stuff> Things said.", parsers.ErrNotPost},
|
{"12:34] <Stuff> Things said.", "no timestamp"},
|
||||||
{"* Stuff Things said.", parsers.ErrNotPost},
|
{"* Stuff Things said.", "no timestamp"},
|
||||||
{"", parsers.ErrNotPost},
|
{"", "no timestamp"},
|
||||||
{"[12:34 <Stuff> Things said.", parsers.ErrNotPost},
|
{"[12:34 <Stuff> Things said.", "incomplete timestamp"},
|
||||||
{"[TE:XT] <Stuff> Things said.", parsers.ErrNotPost},
|
{"[TE:XT] <Stuff> Things said.", "invalid number in timestamp"},
|
||||||
{"[10] <Stuff> Things said.", parsers.ErrNotPost},
|
{"[10] <Stuff> Things said.", "invalid timestamp"},
|
||||||
{"[12:34:56:789] <Stuff> Things said.", nil},
|
{"[12:34:56:789] <Stuff> Things said.", ""},
|
||||||
{"[12:34:56.789] <Stuff> Things said.", parsers.ErrNotPost},
|
{"[12:34:56.789] <Stuff> Things said.", "invalid number in timestamp"},
|
||||||
{"[12:34] <Stuff>", parsers.ErrNotPost},
|
{"[12:34] <Stuff>", "post is empty"},
|
||||||
{"[12:34] * Stuff", parsers.ErrNotPost},
|
{"[12:34] * Stuff", "post is empty"},
|
||||||
{"[12:34] =Scene=", parsers.ErrNotPost},
|
{"[12:34] =Scene=", "line is neither action nor text post"},
|
||||||
{"[12:34] <=Scene=>", parsers.ErrNotPost},
|
{"[12:34] <=Scene=>", "post is empty"},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, row := range table {
|
for i, row := range table {
|
||||||
t.Run(fmt.Sprintf("Row_%d", i), func(t *testing.T) {
|
t.Run(fmt.Sprintf("Row_%d", i), func(t *testing.T) {
|
||||||
_, err := parsers.MircPost(row.Input, time.Now(), models.Post{})
|
_, err := parsers.MircPost(row.Input, time.Now(), models.Post{})
|
||||||
|
|
||||||
assert.Equal(t, row.Err, err, "Error should match")
|
errProblem := ""
|
||||||
|
if err != nil {
|
||||||
|
if e2, ok := err.(*parsers.ParseError); ok {
|
||||||
|
errProblem = e2.Problem
|
||||||
|
} else {
|
||||||
|
errProblem = err.Error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, row.Err, errProblem, "Error should match")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,7 +169,7 @@ func TestMircLogErrors(t *testing.T) {
|
|||||||
_, err2 := parsers.MircLog("\n\n\n\n\n[14:57]* Stuff \n\t\r\n", time.Time{}, true)
|
_, err2 := parsers.MircLog("\n\n\n\n\n[14:57]* Stuff \n\t\r\n", time.Time{}, true)
|
||||||
|
|
||||||
assert.Equal(t, parsers.ErrEmptyLog, err1)
|
assert.Equal(t, parsers.ErrEmptyLog, err1)
|
||||||
assert.Equal(t, parsers.ErrNotPost, err2)
|
assert.True(t, parsers.IsParseError(err2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseDate(t *testing.T, date string) time.Time {
|
func parseDate(t *testing.T, date string) time.Time {
|
||||||
|
|||||||
Reference in New Issue
Block a user