Compare commits

..
Author SHA1 Message Date
gisle 6ce38d61b1 fix log importer not refreshing character list.
continuous-integration/drone/push Build is passing
2020-08-16 11:49:39 +02:00
gisle dc3f77f8da update drone ci.
continuous-integration/drone/push Build is passing
2020-08-09 16:09:11 +02:00
gisle 6321ea1893 update drone ci.
continuous-integration/drone/push Build is failing
2020-08-09 16:08:11 +02:00
gisle 9b346fb97b update drone ci.
continuous-integration/drone/push Build is passing
2020-08-09 16:07:44 +02:00
gisle ed21b7c8cd improve parser error. 2020-08-09 16:01:45 +02:00
6 changed files with 136 additions and 57 deletions
+34 -16
View File
@@ -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
+3
View File
@@ -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
View File
@@ -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
View File
@@ -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:
+42 -9
View File
@@ -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",
}
} }
} }
+26 -17
View File
@@ -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 {