Skip to content

Commit

Permalink
Merge pull request #40 from rokwire/develop
Browse files Browse the repository at this point in the history
merge develop into main
  • Loading branch information
stefanvit authored Jun 7, 2024
2 parents 7aec0ad + e6dd4fd commit 3d6d94a
Show file tree
Hide file tree
Showing 9 changed files with 383 additions and 10 deletions.
4 changes: 2 additions & 2 deletions .secrets.baseline
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,10 @@
"filename": "main.go",
"hashed_secret": "a23be006fadbd85ce60d87580e3f127c58a824ce",
"is_verified": false,
"line_number": 139,
"line_number": 148,
"is_secret": false
}
]
},
"generated_at": "2023-03-02T12:03:59Z"
"generated_at": "2024-06-07T12:23:03Z"
}
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
## [1.5.0] - 2024-07-26
### Added
- Remove user data [#38](https://github.com/rokwire/wellness-building-block/issues/38)

## [1.4.0] - 2024-02-20
### Fixed
Expand Down
26 changes: 22 additions & 4 deletions core/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,38 +17,56 @@ package core
import (
"log"
"sync"

"github.com/rokwire/logging-library-go/v2/logs"
)

// Application represents the core application code based on hexagonal architecture
type Application struct {
version string
build string

logger *logs.Logger

cacheLock *sync.Mutex

Services Services //expose to the drivers adapters

storage Storage
core Core
notifications Notifications

//TODO - remove this when applied to all environemnts
multiTenancyAppID string
multiTenancyOrgID string

deleteDataLogic deleteDataLogic
}

// Start starts the core part of the application
func (app *Application) Start() {
err := app.MigrateMessageIDs()
err := app.deleteDataLogic.start()
if err != nil {
log.Fatalf("error on starting the delete data logic - %s", err)
}

err = app.MigrateMessageIDs()
if err != nil {
log.Printf("error on migrate message ids - %s", err)
}
}

// NewApplication creates new Application
func NewApplication(version string, build string, storage Storage, notifications Notifications, mtAppID string, mtOrgID string) *Application {
func NewApplication(version string, build string,
logger *logs.Logger, storage Storage,
core Core, notifications Notifications, mtAppID string, mtOrgID string) *Application {
cacheLock := &sync.Mutex{}
application := Application{version: version, build: build, cacheLock: cacheLock, storage: storage,
notifications: notifications, multiTenancyAppID: mtAppID, multiTenancyOrgID: mtOrgID}

deleteDataLogic := deleteDataLogic{logger: logger, coreAdapter: core, storage: storage}

application := Application{version: version, build: build, logger: logger, cacheLock: cacheLock, storage: storage,
core: core, notifications: notifications, multiTenancyAppID: mtAppID, multiTenancyOrgID: mtOrgID,
deleteDataLogic: deleteDataLogic}

// add the drivers ports/interfaces
application.Services = &servicesImpl{app: &application}
Expand Down
191 changes: 191 additions & 0 deletions core/application_delete_data_logic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
/*
* Copyright (c) 2020 Board of Trustees of the University of Illinois.
* All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package core

import (
"time"
"wellness/core/model"

"github.com/rokwire/logging-library-go/v2/logs"
)

type deleteDataLogic struct {
logger *logs.Logger

storage Storage
coreAdapter Core

//delete data timer
dailyDeleteTimer *time.Timer
timerDone chan bool
}

func (d deleteDataLogic) start() error {

// set up web tools timer
go d.setupTimerForDelete()

return nil
}

func (d deleteDataLogic) setupTimerForDelete() {
d.logger.Info("Delete data timer")

//cancel if active
if d.dailyDeleteTimer != nil {
d.logger.Info("setupTimerForDelete -> there is active timer, so cancel it")

d.timerDone <- true
d.dailyDeleteTimer.Stop()
}

//wait until it is the correct moment from the day
location, err := time.LoadLocation("America/Chicago")
if err != nil {
d.logger.Errorf("Error getting location:%s\n", err.Error())
}
now := time.Now().In(location)
d.logger.Infof("setupTimerForDelete -> now - hours:%d minutes:%d seconds:%d\n", now.Hour(), now.Minute(), now.Second())

nowSecondsInDay := 60*60*now.Hour() + 60*now.Minute() + now.Second()
desiredMoment := 14400 //4 AM

var durationInSeconds int
d.logger.Infof("setupTimerForDelete -> nowSecondsInDay:%d desiredMoment:%d\n", nowSecondsInDay, desiredMoment)
if nowSecondsInDay <= desiredMoment {
d.logger.Infof("setupTimerForDelete -> not delete process today, so the first process will be today")
durationInSeconds = desiredMoment - nowSecondsInDay
} else {
d.logger.Infof("setupTimerForDelete -> the delete process has already been processed today, so the first process will be tomorrow")
leftToday := 86400 - nowSecondsInDay
durationInSeconds = leftToday + desiredMoment // the time which left today + desired moment from tomorrow
}
//log.Println(durationInSeconds)
//duration := time.Second * time.Duration(3)
duration := time.Second * time.Duration(durationInSeconds)
d.logger.Infof("setupTimerForDelete -> first call after %s", duration)

d.dailyDeleteTimer = time.NewTimer(duration)
select {
case <-d.dailyDeleteTimer.C:
d.logger.Info("setupTimerForDelete -> delete timer expired")
d.dailyDeleteTimer = nil

d.process()
case <-d.timerDone:
// timer aborted
d.logger.Info("setupTimerForDelete -> delete timer aborted")
d.dailyDeleteTimer = nil
}
}

func (d deleteDataLogic) process() {
d.logger.Info("Deleting data process")

//process work
d.processDelete()

//generate new processing after 24 hours
duration := time.Hour * 24
d.logger.Infof("Deleting data process -> next call after %s", duration)
d.dailyDeleteTimer = time.NewTimer(duration)
select {
case <-d.dailyDeleteTimer.C:
d.logger.Info("Deleting data process -> timer expired")
d.dailyDeleteTimer = nil

d.process()
case <-d.timerDone:
// timer aborted
d.logger.Info("Deleting data process -> timer aborted")
d.dailyDeleteTimer = nil
}
}

func (d deleteDataLogic) processDelete() {

//load deleted accounts
deletedMemberships, err := d.coreAdapter.LoadDeletedMemberships()

if err != nil {
d.logger.Errorf("error on loading deleted accounts - %s", err)
return
}

//process by app org

for _, appOrgSection := range deletedMemberships {
d.logger.Infof("delete - [app-id:%s org-id:%s]", appOrgSection.AppID, appOrgSection.OrgID)

accountsIDs := d.getAccountsIDs(appOrgSection.Memberships)
if len(accountsIDs) == 0 {
d.logger.Info("no accounts for deletion")
continue
}

d.logger.Infof("accounts for deletion - %s", accountsIDs)

//delete the data
d.deleteAppOrgUsersData(appOrgSection.AppID, appOrgSection.OrgID, accountsIDs)
}
}

func (d deleteDataLogic) deleteAppOrgUsersData(appID string, orgID string, accountsIDs []string) {

// delete the todo categories
err := d.storage.DeleteTodoCategoriesForUsers(appID, orgID, accountsIDs)
if err != nil {
d.logger.Errorf("error deleting todo categories for users - %s", err)
return
}

// delete the todo entries
err = d.storage.DeleteTodoEntriesForUsers(appID, orgID, accountsIDs)
if err != nil {
d.logger.Errorf("error deleting todo entries for users - %s", err)
return
}

// delete the rings
err = d.storage.DeleteRingsForUsers(appID, orgID, accountsIDs)
if err != nil {
d.logger.Errorf("error deleting rings for users - %s", err)
return
}

// delete the rings records
err = d.storage.DeleteRingsForUsers(appID, orgID, accountsIDs)
if err != nil {
d.logger.Errorf("error deleting rings records for users - %s", err)
return
}
}

func (d deleteDataLogic) getAccountsIDs(memberships []model.DeletedMembership) []string {
res := make([]string, len(memberships))
for i, item := range memberships {
res[i] = item.AccountID
}
return res
}

// deleteLogic creates new deleteLogic
func deleteLogic( /*coreAdapter Core,*/ logger *logs.Logger) deleteDataLogic {
timerDone := make(chan bool)
return deleteDataLogic{ /*coreAdapter: coreAdapter,*/ timerDone: timerDone, logger: logger}
}
9 changes: 9 additions & 0 deletions core/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ type Storage interface {
CreateTodoCategory(appID string, orgID string, userID string, category *model.TodoCategory) (*model.TodoCategory, error)
UpdateTodoCategory(appID string, orgID string, userID string, category *model.TodoCategory) (*model.TodoCategory, error)
DeleteTodoCategory(appID string, orgID string, userID string, id string) error
DeleteTodoCategoriesForUsers(appID string, orgID string, accountsIDs []string) error

GetTodoEntriesWithCurrentReminderTime(context storage.TransactionContext, reminderTime time.Time) ([]model.TodoEntry, error)
GetTodoEntriesWithCurrentDueTime(context storage.TransactionContext, dueTime time.Time) ([]model.TodoEntry, error)
Expand All @@ -167,23 +168,31 @@ type Storage interface {
UpdateTodoEntriesTaskTime(context storage.TransactionContext, ids []string, taskTime time.Time) error
DeleteTodoEntry(context storage.TransactionContext, appID string, orgID string, userID string, id string) error
DeleteCompletedTodoEntries(appID string, orgID string, userID string) error
DeleteTodoEntriesForUsers(appID string, orgID string, accountsIDs []string) error

GetRings(appID string, orgID string, userID string) ([]model.Ring, error)
GetRing(appID string, orgID string, userID string, id string) (*model.Ring, error)
CreateRing(appID string, orgID string, userID string, category *model.Ring) (*model.Ring, error)
DeleteRing(appID string, orgID string, userID string, id string) error
CreateRingHistory(appID string, orgID string, userID string, ringID string, ringHistory *model.RingHistoryEntry) (*model.Ring, error)
DeleteRingHistory(appID string, orgID string, userID string, ringID string, ringHistoryID string) (*model.Ring, error)
DeleteRingsForUsers(appID string, orgID string, accountsIDs []string) error

GetRingsRecords(appID string, orgID string, userID string, ringID *string, startDateEpoch *int64, endDateEpoch *int64, offset *int64, limit *int64, order *string) ([]model.RingRecord, error)
GetRingsRecord(appID string, orgID string, userID string, id string) (*model.RingRecord, error)
CreateRingsRecord(appID string, orgID string, userID string, record *model.RingRecord) (*model.RingRecord, error)
UpdateRingsRecord(appID string, orgID string, userID string, record *model.RingRecord) (*model.RingRecord, error)
DeleteRingsRecords(appID string, orgID string, userID string, ringID *string, recordID *string) error
DeleteRingsRecordsForUsers(appID string, orgID string, accountsIDs []string) error
}

// Notifications wrapper
type Notifications interface {
SendNotification(recipients []model.NotificationRecipient, topic *string, title string, text string, appID string, orgID string, time *int64, data map[string]string) (*string, error)
DeleteNotification(appID string, orgID string, id string) error
}

// Core exposes Core APIs for the driver adapters
type Core interface {
LoadDeletedMemberships() ([]model.DeletedUserData, error)
}
13 changes: 13 additions & 0 deletions core/model/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,16 @@ type Config struct {
CoreBBHost string
ServiceURL string
}

// DeletedUserData represents a user-deleted
type DeletedUserData struct {
AppID string `json:"app_id"`
Memberships []DeletedMembership `json:"memberships"`
OrgID string `json:"org_id"`
}

// DeletedMembership defines model for DeletedMembership.
type DeletedMembership struct {
AccountID string `json:"account_id"`
Context *map[string]interface{} `json:"context,omitempty"`
}
Loading

0 comments on commit 3d6d94a

Please sign in to comment.