Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ID-34]Remove user data #35

Merged
merged 13 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from 11 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
2 changes: 1 addition & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: '1.21'
go-version: '1.22'
check-latest: true

- name: Build
Expand Down
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]
### Added
- Remove user data [#34](https://github.com/rokwire/surveys-building-block/issues/34)

## [1.3.0] - 2023-09-20
### Added
- Reintroduce survey responses admin API [#27](https://github.com/rokwire/surveys-building-block/issues/27)
Expand Down
180 changes: 180 additions & 0 deletions core/app_delete_data_logic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
/*
* 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 (
"fmt"

"application/core/interfaces"
"application/core/model"
corebb "application/driven/core"
"time"

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

type deleteDataLogic struct {
logger logs.Logger

serviceID string
core *corebb.Adapter

storage interfaces.Storage

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

func (d deleteDataLogic) start() error {

//2. 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.core.LoadDeletedMemberships()
if err != nil {
d.logger.Errorf("error on loading deleted accounts - %s", err)
return
}
fmt.Print(deletedMemberships)
//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 survey responses
err := d.storage.DeleteSurveyResponsesWithIDs(appID, orgID, accountsIDs)
if err != nil {
d.logger.Errorf("error deleting the survey responses - %s", err)
return
}

// delete surveys
err = d.storage.DeleteSurveysWithIDs(appID, orgID, accountsIDs)
if err != nil {
d.logger.Errorf("error deleting the surveys - %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 corebb.Adapter, logger logs.Logger) deleteDataLogic {
timerDone := make(chan bool)
return deleteDataLogic{core: &coreAdapter, timerDone: timerDone, logger: logger}
}
20 changes: 14 additions & 6 deletions core/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ package core
import (
"application/core/interfaces"
"application/core/model"
corebb "application/driven/core"

"github.com/rokwire/core-auth-library-go/v3/authutils"
stefanvit marked this conversation as resolved.
Show resolved Hide resolved
"github.com/rokwire/core-auth-library-go/v2/authutils"
"github.com/rokwire/logging-library-go/v2/errors"
"github.com/rokwire/logging-library-go/v2/logs"
"github.com/rokwire/logging-library-go/v2/logutils"
Expand Down Expand Up @@ -52,16 +53,19 @@ type Application struct {

logger *logs.Logger

storage interfaces.Storage
notifications interfaces.Notifications
calendar interfaces.Calendar
storage interfaces.Storage
notifications interfaces.Notifications
calendar interfaces.Calendar
corebb *corebb.Adapter
deleteDataLogic deleteDataLogic
}

// Start starts the core part of the application
func (a *Application) Start() {
//set storage listener
storageListener := storageListener{app: a}
a.storage.RegisterStorageListener(&storageListener)
a.deleteDataLogic.start()
}

// GetEnvConfigs retrieves the cached database env configs
Expand All @@ -78,8 +82,12 @@ func (a *Application) GetEnvConfigs() (*model.EnvConfigData, error) {
}

// NewApplication creates new Application
func NewApplication(version string, build string, storage interfaces.Storage, notifications interfaces.Notifications, calendar interfaces.Calendar, logger *logs.Logger) *Application {
application := Application{version: version, build: build, storage: storage, notifications: notifications, calendar: calendar, logger: logger}
func NewApplication(version string, build string, storage interfaces.Storage, notifications interfaces.Notifications, calendar interfaces.Calendar,
coreBB *corebb.Adapter, serviceID string, logger *logs.Logger) *Application {
deleteDataLogic := deleteDataLogic{logger: *logger, core: coreBB, serviceID: serviceID, storage: storage}

application := Application{version: version, build: build, storage: storage, notifications: notifications,
calendar: calendar, deleteDataLogic: deleteDataLogic, logger: logger}

//add the drivers ports/interfaces
application.Default = newAppDefault(&application)
Expand Down
4 changes: 2 additions & 2 deletions core/application_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (

"github.com/stretchr/testify/mock"

"github.com/rokwire/core-auth-library-go/v3/authutils"
stefanvit marked this conversation as resolved.
Show resolved Hide resolved
"github.com/rokwire/core-auth-library-go/v2/authutils"
"github.com/rokwire/logging-library-go/v2/logs"
)

Expand All @@ -37,7 +37,7 @@ const (
func buildTestApplication(storage interfaces.Storage) *core.Application {
loggerOpts := logs.LoggerOpts{SuppressRequests: logs.NewStandardHealthCheckHTTPRequestProperties(serviceID + "/version")}
logger := logs.NewLogger(serviceID, &loggerOpts)
return core.NewApplication("1.1.1", "build", storage, nil, nil, logger)
return core.NewApplication("1.1.1", "build", storage, nil, nil, nil, "", logger)
}

func TestApplication_Start(t *testing.T) {
Expand Down
5 changes: 5 additions & 0 deletions core/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,8 @@ type Shared interface {
isEventAdmin(orgID string, appID string, eventID string, userID string, externalIDs map[string]string) (bool, error)
hasAttendedEvent(orgID string, appID string, eventID string, userID string, externalIDs map[string]string) (bool, error)
}

// Core exposes Core APIs for the driver adapters
type Core interface {
LoadDeletedMemberships() ([]model.DeletedUserData, error)
}
2 changes: 2 additions & 0 deletions core/interfaces/driven.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,15 @@ type Storage interface {
CreateSurvey(survey model.Survey) (*model.Survey, error)
UpdateSurvey(survey model.Survey, admin bool) error
DeleteSurvey(id string, orgID string, appID string, creatorID string, admin bool) error
DeleteSurveysWithIDs(appID string, orgID string, accountsIDs []string) error

GetSurveyResponse(id string, orgID string, appID string, userID string) (*model.SurveyResponse, error)
GetSurveyResponses(orgID *string, appID *string, userID *string, surveyIDs []string, surveyTypes []string, startDate *time.Time, endDate *time.Time, limit *int, offset *int) ([]model.SurveyResponse, error)
CreateSurveyResponse(surveyResponse model.SurveyResponse) (*model.SurveyResponse, error)
UpdateSurveyResponse(surveyResponse model.SurveyResponse) error
DeleteSurveyResponse(id string, orgID string, appID string, userID string) error
DeleteSurveyResponses(orgID string, appID string, userID string, surveyIDs []string, surveyTypes []string, startDate *time.Time, endDate *time.Time) error
DeleteSurveyResponsesWithIDs(appID string, orgID string, accountsIDs []string) error

GetAlertContacts(orgID string, appID string) ([]model.AlertContact, error)
GetAlertContact(id string, orgID string, appID string) (*model.AlertContact, error)
Expand Down
Loading
Loading