Skip to content

Commit

Permalink
Merge pull request #22 from TetAlius/develop
Browse files Browse the repository at this point in the history
Release 0.1.3
  • Loading branch information
TetAlius authored Jun 11, 2018
2 parents ad4e358 + 0e431f8 commit 497e75a
Show file tree
Hide file tree
Showing 17 changed files with 684 additions and 251 deletions.
111 changes: 16 additions & 95 deletions api/api.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
package api

import (
"errors"
"fmt"
"reflect"
"strings"

"time"

"os"

log "github.com/TetAlius/GoSyncMyCalendars/logger"
"github.com/getsentry/raven-go"
"github.com/google/uuid"
)
Expand All @@ -26,6 +24,19 @@ const (
maxBackoff = 5
)

// tagOptions is the string following a comma in a struct field's
// tag, or the empty string. It does not include the leading comma.
type tagOptions string

// parseTag splits a struct field's tag into its name and
// comma-separated options.
func parseTag(tag string) (string, tagOptions) {
if idx := strings.Index(tag, ","); idx != -1 {
return tag[:idx], tagOptions(tag[idx+1:])
}
return tag, tagOptions("")
}

type AccountManager interface {
Refresh() error

Expand Down Expand Up @@ -85,11 +96,12 @@ type EventManager interface {
MarkWrong()
GetState() int
SetState(int)
PrepareFields()
CanProcessAgain() bool
IncrementBackoff()
SetInternalID(int)
GetInternalID() int

setAllDay()
}

type SubscriptionManager interface {
Expand All @@ -113,97 +125,6 @@ func (err RefreshError) Error() string {
return fmt.Sprintf("code: %s. message: %s", err.Code, err.Message)
}

func Convert(from EventManager, to EventManager) (err error) {
err = convert(from, to)
if err != nil {
return errors.New(fmt.Sprintf("could not convert events: %s", err.Error()))
}
to.PrepareFields()
return
}

func convert(in interface{}, out interface{}) (err error) {
log.Debugln("Converting...")
tag := "sync"

v := reflect.ValueOf(in)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}

// we only accept structs
if v.Kind() != reflect.Struct {
return errors.New(fmt.Sprintf("conver only accepts structs, got %T", v))
}

typ := v.Type()
for i := 0; i < v.NumField(); i++ {
fi := typ.Field(i)
if tagv := fi.Tag.Get(tag); tagv != "" && tagv != "-" {
err := setField(out, tagv, v.Field(i).Interface())
if err != nil {
return err
}
}
}
return
}

func setField(obj interface{}, name string, value interface{}) error {
structValue := reflect.ValueOf(obj).Elem()
structFieldValue := structValue.FieldByName(name)

if !structFieldValue.IsValid() {
return errors.New(fmt.Sprintf("no such field: %s in obj", name))
}

if !structFieldValue.CanSet() {
return errors.New(fmt.Sprintf("cannot set %s field value", name))
}

structFieldType := structFieldValue.Type()
val := reflect.ValueOf(value)
if structFieldType != val.Type() {
return errors.New(fmt.Sprintf("provided value type didn't match obj field type"))
}
//TODO: error here
sentry := sentryClient()
sentry.CapturePanicAndWait(func() { structFieldValue.Set(val) }, map[string]string{"api": "setField"})

return nil
}

func PrepareSync(calendar CalendarManager) (err error) {
err = calendar.GetAccount().Refresh()
if err != nil {
log.Errorf("error refreshing account: %s", err.Error())
return
}

cal, err := calendar.GetAccount().GetCalendar(calendar.GetID())
convert(cal, calendar)
for _, calen := range calendar.GetCalendars() {
err := convert(calendar, calen)
if err != nil {
log.Errorf("error converting info: %s", err.Error())
return err
}
log.Debugf("Name1: %s Name2: %s", calendar.GetName(), calen.GetName())
err = calen.GetAccount().Refresh()
if err != nil {
log.Errorf("error refreshing account calendar: %s error: %s", calen.GetID(), err.Error())
return err
}
err = calen.Update()

if err != nil {
log.Errorf("error updating calendar: %s error: %s", calen.GetID(), err.Error())
return err
}
}
return
}

func GetChangeType(onCloud bool, onDB bool) int {
if onCloud && !onDB {
return Created
Expand Down
21 changes: 7 additions & 14 deletions api/google_calendar.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,16 +155,13 @@ func (calendar *GoogleCalendar) GetAllEvents() (events []EventManager, err error
err = json.Unmarshal(contents, &eventList)

//events = new([]EventManager)
for _, s := range eventList.Events {
s.SetCalendar(calendar)
for _, event := range eventList.Events {
event.SetCalendar(calendar)
// ignore cancelled events
if s.Status != "cancelled" {

err := s.extractTime()
if err != nil {
return nil, err
}
events = append(events, s)
if event.Status != "cancelled" {
event.setAllDay()
//TODO: this status
events = append(events, event)
}
}
return events, err
Expand Down Expand Up @@ -207,13 +204,9 @@ func (calendar *GoogleCalendar) GetEvent(eventID string) (event EventManager, er
log.Warningf("GOOGLE EVENT: %s", contents)
//TODO: this part
if eventResponse.Status != "cancelled" {
err = eventResponse.extractTime()
if err != nil {
return
}

eventResponse.SetCalendar(calendar)
event = eventResponse
event.setAllDay()
} else {
return nil, &customErrors.NotFoundError{Message: fmt.Sprintf("event with id: %s not found", eventID)}
}
Expand Down
146 changes: 107 additions & 39 deletions api/google_event.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import (

"time"

"reflect"

conv "github.com/TetAlius/GoSyncMyCalendars/convert"
log "github.com/TetAlius/GoSyncMyCalendars/logger"
"github.com/TetAlius/GoSyncMyCalendars/util"
)
Expand Down Expand Up @@ -43,11 +46,11 @@ func (event *GoogleEvent) Create() (err error) {
}
err = createGoogleResponseError(contents)
if err != nil {
log.Errorf("error creating event: %s", err.Error())
return err
}

err = json.Unmarshal(contents, &event)
err = event.extractTime()
return
}

Expand Down Expand Up @@ -84,7 +87,6 @@ func (event *GoogleEvent) Update() (err error) {
}

err = json.Unmarshal(contents, &event)
err = event.extractTime()
return
}

Expand Down Expand Up @@ -132,18 +134,6 @@ func (event *GoogleEvent) GetRelations() []EventManager {
return event.relations
}

func (event *GoogleEvent) PrepareFields() {
var startDate, endDate string
if event.IsAllDay {
startDate = event.StartsAt.Format("2006-01-02")
endDate = event.EndsAt.Format("2006-01-02")
}

event.Start = &GoogleTime{startDate, event.StartsAt.Format(time.RFC3339), "UTC"}
event.End = &GoogleTime{endDate, event.EndsAt.Format(time.RFC3339), "UTC"}
return
}

func (event *GoogleEvent) CanProcessAgain() bool {
return event.exponentialBackoff < maxBackoff
}
Expand Down Expand Up @@ -196,35 +186,113 @@ func (event *GoogleEvent) GetUpdatedAt() (t time.Time, err error) {
return t.UTC(), nil
}

func (event *GoogleEvent) extractTime() (err error) {
var start, end, format string
sentry := sentryClient()
recoveredPanic, sentryID := sentry.CapturePanicAndWait(func() {
if len(event.Start.Date) != 0 && len(event.End.Date) != 0 {
event.IsAllDay = true
start = event.Start.Date
end = event.End.Date
format = "2006-01-02"

} else {
event.IsAllDay = false
start = event.Start.DateTime
end = event.End.DateTime
format = time.RFC3339
func (date *GoogleTime) UnmarshalJSON(b []byte) error {
var s map[string]string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
for key, value := range s {
switch key {
case "date":
date.IsAllDay = true
t, err := time.Parse("2006-01-02", value)
if err != nil {
return err
}
date.Date = t.UTC()
case "dateTime":
date.IsAllDay = false
t, err := time.Parse(time.RFC3339, value)
if err != nil {
return err
}
date.DateTime = t.UTC()
}
}, map[string]string{"api": "google"})
if recoveredPanic != nil {
log.Errorf("panic recovered with sentry ID: %s", sentryID)
return fmt.Errorf("panic was launched")
}
date.TimeZone = time.UTC

event.StartsAt, err = time.Parse(format, start)
if err != nil {
return errors.New(fmt.Sprintf("error parsing start time: %s %s", start, err.Error()))
return nil
}

func (date *GoogleTime) MarshalJSON() ([]byte, error) {
if date.DateTime.IsZero() && date.Date.IsZero() {
return bytes.NewBufferString("{}").Bytes(), nil
}
var jsonValue string
var name string
if date.IsAllDay {
name = "Date"
jsonValue = date.Date.UTC().Format("2006-01-02")
} else {
name = "DateTime"
jsonValue = date.DateTime.UTC().Format(time.RFC3339)
}
event.EndsAt, err = time.Parse(format, end)
field, ok := reflect.TypeOf(date).Elem().FieldByName(name)
if !ok {
return nil, fmt.Errorf("could not retrieve field %s", name)
}
tag, _ := parseTag(field.Tag.Get("json"))
buffer := bytes.NewBufferString("{")
_, err := buffer.WriteString(fmt.Sprintf(`"%s":"%s"}`, tag, jsonValue))
if err != nil {
return errors.New(fmt.Sprintf("error parsing end time: %s %s", end, err.Error()))
return nil, err
}
return
return buffer.Bytes(), nil
}

func (date *GoogleTime) Deconvert() interface{} {
m := make(map[string]interface{})
var value time.Time
if date.IsAllDay {
value = date.Date.UTC()
} else {
value = date.DateTime.UTC()
}
field, ok := reflect.TypeOf(date).Elem().FieldByName("DateTime")
if !ok {
return nil
}
tag, _ := parseTag(field.Tag.Get("convert"))
m[tag] = value
field, ok = reflect.TypeOf(date).Elem().FieldByName("IsAllDay")
if !ok {
return nil
}
tag, _ = parseTag(field.Tag.Get("convert"))
m[tag] = date.IsAllDay

field, ok = reflect.TypeOf(date).Elem().FieldByName("TimeZone")
if !ok {
return nil
}
tag, _ = parseTag(field.Tag.Get("convert"))
m[tag] = date.TimeZone
return m
}

func (*GoogleTime) Convert(m interface{}, tag string, opts string) (conv.Converter, error) {
d := m.(map[string]interface{})

dateTime, ok := d["dateTime"].(time.Time)
if !ok {
return nil, errors.New("incorrect type of field dateTime")
}
isAllDay, ok := d["isAllDay"].(bool)
if !ok {
return nil, errors.New("incorrect type of field isAllDay")
}
timeZone, ok := d["timeZone"].(*time.Location)
if !ok {
return nil, errors.New("incorrect type of field timeZone")
}

return &GoogleTime{DateTime: dateTime, Date: dateTime, TimeZone: timeZone, IsAllDay: isAllDay}, nil
}

func (event *GoogleEvent) setAllDay() {
if event.Start == nil && event.End == nil {
event.IsAllDay = false
return
}
event.IsAllDay = event.Start.IsAllDay
}
Loading

0 comments on commit 497e75a

Please sign in to comment.