diff --git a/CHANGELOG.md b/CHANGELOG.md index da3463a..467828c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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.9.0] - 2024-007-26 +### Added +- Add "complete" field to show if the survey is completed [#61](https://github.com/rokwire/surveys-building-block/issues/61) ## [1.8.2] - 2024-007-26 ### Fixed - Fix Get /surveys [#58](https://github.com/rokwire/surveys-building-block/issues/58) diff --git a/SECURITY.md b/SECURITY.md index c432092..f1feef5 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,8 +6,8 @@ Patches for **Surveys Building Block** in this repository will only be applied t | Version | Supported | | ------- | ------------------ | -| 1.8.2 | :white_check_mark: | -| < 1.8.2 | :x: | +| 1.9.0 | :white_check_mark: | +| < 1.9.0 | :x: | ## Reporting a Bug or Vulnerability diff --git a/core/app_admin.go b/core/app_admin.go index 5f8f556..63b64c1 100644 --- a/core/app_admin.go +++ b/core/app_admin.go @@ -37,8 +37,8 @@ func (a appAdmin) GetSurvey(id string, orgID string, appID string) (*model.Surve } // GetSurvey returns surveys matching the provided query -func (a appAdmin) GetSurveys(orgID string, appID string, creatorID *string, surveyIDs []string, surveyTypes []string, calendarEventID string, limit *int, offset *int, filter *model.SurveyTimeFilter, public *bool, archived *bool) ([]model.Survey, []model.SurveyResponse, error) { - return a.app.shared.getSurveys(orgID, appID, creatorID, surveyIDs, surveyTypes, calendarEventID, limit, offset, filter, public, archived) +func (a appAdmin) GetSurveys(orgID string, appID string, creatorID *string, surveyIDs []string, surveyTypes []string, calendarEventID string, limit *int, offset *int, filter *model.SurveyTimeFilter, public *bool, archived *bool, completed *bool) ([]model.Survey, []model.SurveyResponse, error) { + return a.app.shared.getSurveys(orgID, appID, creatorID, surveyIDs, surveyTypes, calendarEventID, limit, offset, filter, public, archived, completed) } // GetAllSurveyResponses returns survey responses matching the provided query diff --git a/core/app_client.go b/core/app_client.go index d67bd55..06e80a4 100644 --- a/core/app_client.go +++ b/core/app_client.go @@ -36,8 +36,8 @@ func (a appClient) GetSurvey(id string, orgID string, appID string) (*model.Surv // GetSurvey returns surveys matching the provided query func (a appClient) GetSurveys(orgID string, appID string, creatorID *string, surveyIDs []string, surveyTypes []string, calendarEventID string, - limit *int, offset *int, filter *model.SurveyTimeFilter, public *bool, archived *bool) ([]model.Survey, []model.SurveyResponse, error) { - return a.app.shared.getSurveys(orgID, appID, creatorID, surveyIDs, surveyTypes, calendarEventID, limit, offset, filter, public, archived) + limit *int, offset *int, filter *model.SurveyTimeFilter, public *bool, archived *bool, completed *bool) ([]model.Survey, []model.SurveyResponse, error) { + return a.app.shared.getSurveys(orgID, appID, creatorID, surveyIDs, surveyTypes, calendarEventID, limit, offset, filter, public, archived, completed) } // CreateSurvey creates a new survey diff --git a/core/app_shared.go b/core/app_shared.go index 55e89d1..8f002de 100644 --- a/core/app_shared.go +++ b/core/app_shared.go @@ -34,8 +34,8 @@ func (a appShared) getSurvey(id string, orgID string, appID string) (*model.Surv return a.app.storage.GetSurvey(id, orgID, appID) } -func (a appShared) getSurveys(orgID string, appID string, creatorID *string, surveyIDs []string, surveyTypes []string, calendarEventID string, limit *int, offset *int, filter *model.SurveyTimeFilter, public *bool, archived *bool) ([]model.Survey, []model.SurveyResponse, error) { - surveys, err := a.app.storage.GetSurveys(orgID, appID, creatorID, surveyIDs, surveyTypes, calendarEventID, limit, offset, filter, public, archived) +func (a appShared) getSurveys(orgID string, appID string, creatorID *string, surveyIDs []string, surveyTypes []string, calendarEventID string, limit *int, offset *int, filter *model.SurveyTimeFilter, public *bool, archived *bool, completed *bool) ([]model.Survey, []model.SurveyResponse, error) { + surveys, err := a.app.storage.GetSurveys(orgID, appID, creatorID, surveyIDs, surveyTypes, calendarEventID, limit, offset, filter, public, archived, nil) if err != nil { return nil, nil, err } diff --git a/core/interfaces.go b/core/interfaces.go index 32b5c4e..b03186d 100644 --- a/core/interfaces.go +++ b/core/interfaces.go @@ -20,7 +20,7 @@ import "application/core/model" type Shared interface { // Surveys getSurvey(id string, orgID string, appID string) (*model.Survey, error) - getSurveys(orgID string, appID string, creatorID *string, surveyIDs []string, surveyTypes []string, calendarEventID string, limit *int, offset *int, filter *model.SurveyTimeFilter, public *bool, archived *bool) ([]model.Survey, []model.SurveyResponse, error) + getSurveys(orgID string, appID string, creatorID *string, surveyIDs []string, surveyTypes []string, calendarEventID string, limit *int, offset *int, filter *model.SurveyTimeFilter, public *bool, archived *bool, completed *bool) ([]model.Survey, []model.SurveyResponse, error) createSurvey(survey model.Survey, externalIDs map[string]string) (*model.Survey, error) updateSurvey(survey model.Survey, userID string, externalIDs map[string]string, admin bool) error deleteSurvey(id string, orgID string, appID string, userID string, externalIDs map[string]string, admin bool) error diff --git a/core/interfaces/core.go b/core/interfaces/core.go index cbd4762..8a09e29 100644 --- a/core/interfaces/core.go +++ b/core/interfaces/core.go @@ -30,7 +30,7 @@ type Default interface { type Client interface { // Surveys GetSurvey(id string, orgID string, appID string) (*model.Survey, error) - GetSurveys(orgID string, appID string, creatorID *string, surveyIDs []string, surveyTypes []string, calendarEventID string, limit *int, offset *int, filter *model.SurveyTimeFilter, public *bool, archived *bool) ([]model.Survey, []model.SurveyResponse, error) + GetSurveys(orgID string, appID string, creatorID *string, surveyIDs []string, surveyTypes []string, calendarEventID string, limit *int, offset *int, filter *model.SurveyTimeFilter, public *bool, archived *bool, completed *bool) ([]model.Survey, []model.SurveyResponse, error) CreateSurvey(survey model.Survey, externalIDs map[string]string) (*model.Survey, error) UpdateSurvey(survey model.Survey, userID string, externalIDs map[string]string) error DeleteSurvey(id string, orgID string, appID string, userID string, externalIDs map[string]string) error @@ -59,7 +59,7 @@ type Admin interface { // Surveys GetSurvey(id string, orgID string, appID string) (*model.Survey, error) - GetSurveys(orgID string, appID string, creatorID *string, surveyIDs []string, surveyTypes []string, calendarEventID string, limit *int, offset *int, filter *model.SurveyTimeFilter, public *bool, archived *bool) ([]model.Survey, []model.SurveyResponse, error) + GetSurveys(orgID string, appID string, creatorID *string, surveyIDs []string, surveyTypes []string, calendarEventID string, limit *int, offset *int, filter *model.SurveyTimeFilter, public *bool, archived *bool, completed *bool) ([]model.Survey, []model.SurveyResponse, error) CreateSurvey(survey model.Survey, externalIDs map[string]string) (*model.Survey, error) UpdateSurvey(survey model.Survey, userID string, externalIDs map[string]string) error DeleteSurvey(id string, orgID string, appID string, userID string, externalIDs map[string]string) error diff --git a/core/interfaces/driven.go b/core/interfaces/driven.go index db88dc3..9c51a0a 100644 --- a/core/interfaces/driven.go +++ b/core/interfaces/driven.go @@ -33,7 +33,7 @@ type Storage interface { DeleteConfig(id string) error GetSurvey(id string, orgID string, appID string) (*model.Survey, error) - GetSurveys(orgID string, appID string, creatorID *string, surveyIDs []string, surveyTypes []string, calendarEventID string, limit *int, offset *int, filter *model.SurveyTimeFilter, public *bool, archived *bool) ([]model.Survey, error) + GetSurveys(orgID string, appID string, creatorID *string, surveyIDs []string, surveyTypes []string, calendarEventID string, limit *int, offset *int, filter *model.SurveyTimeFilter, public *bool, archived *bool, completed *bool) ([]model.Survey, error) 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 diff --git a/core/interfaces/mocks/Storage.go b/core/interfaces/mocks/Storage.go index 266a3fe..9753360 100644 --- a/core/interfaces/mocks/Storage.go +++ b/core/interfaces/mocks/Storage.go @@ -503,9 +503,9 @@ func (_m *Storage) GetSurveyResponses(orgID *string, appID *string, userID *stri return r0, r1 } -// GetSurveys provides a mock function with given fields: orgID, appID, creatorID, surveyIDs, surveyTypes, calendarEventID, limit, offset, filter, public, archived -func (_m *Storage) GetSurveys(orgID string, appID string, creatorID *string, surveyIDs []string, surveyTypes []string, calendarEventID string, limit *int, offset *int, filter *model.SurveyTimeFilter, public *bool, archived *bool) ([]model.Survey, error) { - ret := _m.Called(orgID, appID, creatorID, surveyIDs, surveyTypes, calendarEventID, limit, offset, filter, public, archived) +// GetSurveys provides a mock function with given fields: orgID, appID, creatorID, surveyIDs, surveyTypes, calendarEventID, limit, offset, filter, public, archived, completed +func (_m *Storage) GetSurveys(orgID string, appID string, creatorID *string, surveyIDs []string, surveyTypes []string, calendarEventID string, limit *int, offset *int, filter *model.SurveyTimeFilter, public *bool, archived *bool, completed *bool) ([]model.Survey, error) { + ret := _m.Called(orgID, appID, creatorID, surveyIDs, surveyTypes, calendarEventID, limit, offset, filter, public, archived, completed) if len(ret) == 0 { panic("no return value specified for GetSurveys") @@ -513,19 +513,19 @@ func (_m *Storage) GetSurveys(orgID string, appID string, creatorID *string, sur var r0 []model.Survey var r1 error - if rf, ok := ret.Get(0).(func(string, string, *string, []string, []string, string, *int, *int, *model.SurveyTimeFilter, *bool, *bool) ([]model.Survey, error)); ok { - return rf(orgID, appID, creatorID, surveyIDs, surveyTypes, calendarEventID, limit, offset, filter, public, archived) + if rf, ok := ret.Get(0).(func(string, string, *string, []string, []string, string, *int, *int, *model.SurveyTimeFilter, *bool, *bool, *bool) ([]model.Survey, error)); ok { + return rf(orgID, appID, creatorID, surveyIDs, surveyTypes, calendarEventID, limit, offset, filter, public, archived, completed) } - if rf, ok := ret.Get(0).(func(string, string, *string, []string, []string, string, *int, *int, *model.SurveyTimeFilter, *bool, *bool) []model.Survey); ok { - r0 = rf(orgID, appID, creatorID, surveyIDs, surveyTypes, calendarEventID, limit, offset, filter, public, archived) + if rf, ok := ret.Get(0).(func(string, string, *string, []string, []string, string, *int, *int, *model.SurveyTimeFilter, *bool, *bool, *bool) []model.Survey); ok { + r0 = rf(orgID, appID, creatorID, surveyIDs, surveyTypes, calendarEventID, limit, offset, filter, public, archived, completed) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]model.Survey) } } - if rf, ok := ret.Get(1).(func(string, string, *string, []string, []string, string, *int, *int, *model.SurveyTimeFilter, *bool, *bool) error); ok { - r1 = rf(orgID, appID, creatorID, surveyIDs, surveyTypes, calendarEventID, limit, offset, filter, public, archived) + if rf, ok := ret.Get(1).(func(string, string, *string, []string, []string, string, *int, *int, *model.SurveyTimeFilter, *bool, *bool, *bool) error); ok { + r1 = rf(orgID, appID, creatorID, surveyIDs, surveyTypes, calendarEventID, limit, offset, filter, public, archived, completed) } else { r1 = ret.Error(1) } diff --git a/core/model/surveys.go b/core/model/surveys.go index 56582f0..17820bb 100644 --- a/core/model/surveys.go +++ b/core/model/surveys.go @@ -250,5 +250,5 @@ type SurveysResponseData struct { Public *bool `json:"public"` Archived *bool `json:"archived"` EstimatedCompletionTime *int `json:"estimated_completion_time"` - Complete *bool `json:"complete"` + Completed *bool `json:"completed"` } diff --git a/driven/storage/adapter_surveys.go b/driven/storage/adapter_surveys.go index f9c636e..0015331 100644 --- a/driven/storage/adapter_surveys.go +++ b/driven/storage/adapter_surveys.go @@ -37,7 +37,7 @@ func (a *Adapter) GetSurvey(id string, orgID string, appID string) (*model.Surve } // GetSurveys gets matching surveys -func (a *Adapter) GetSurveys(orgID string, appID string, creatorID *string, surveyIDs []string, surveyTypes []string, calendarEventID string, limit *int, offset *int, timeFilter *model.SurveyTimeFilter, public *bool, archived *bool) ([]model.Survey, error) { +func (a *Adapter) GetSurveys(orgID string, appID string, creatorID *string, surveyIDs []string, surveyTypes []string, calendarEventID string, limit *int, offset *int, timeFilter *model.SurveyTimeFilter, public *bool, archived *bool, completed *bool) ([]model.Survey, error) { filter := bson.D{ {Key: "org_id", Value: orgID}, {Key: "app_id", Value: appID}, diff --git a/driver/web/apis_admin.go b/driver/web/apis_admin.go index 378df31..22d567c 100644 --- a/driver/web/apis_admin.go +++ b/driver/web/apis_admin.go @@ -258,13 +258,25 @@ func (h AdminAPIsHandler) getSurveys(l *logs.Log, r *http.Request, claims *token archived = &valueArchived } + completedStr := r.URL.Query().Get("completed") + + var completed *bool + + if completedStr != "" { + valueCompleted, err := strconv.ParseBool(completedStr) + if err != nil { + return l.HTTPResponseErrorAction(logutils.ActionGet, model.TypeSurvey, nil, err, http.StatusInternalServerError, true) + } + completed = &valueCompleted + } + surveys, surverysRsponse, err := h.app.Admin.GetSurveys(claims.OrgID, claims.AppID, nil, surveyIDs, surveyTypes, calendarEventID, - &limit, &offset, filter, public, archived) + &limit, &offset, filter, public, archived, completed) if err != nil { return l.HTTPResponseErrorAction(logutils.ActionGet, model.TypeSurvey, nil, err, http.StatusInternalServerError, true) } - resData := getSurveysResData(surveys, surverysRsponse) + resData := getSurveysResData(surveys, surverysRsponse, completed) sort.Slice(resData, func(i, j int) bool { return resData[i].DateCreated.After(resData[j].DateCreated) }) diff --git a/driver/web/apis_client.go b/driver/web/apis_client.go index 4df3d39..45a6657 100644 --- a/driver/web/apis_client.go +++ b/driver/web/apis_client.go @@ -133,13 +133,25 @@ func (h ClientAPIsHandler) getSurveys(l *logs.Log, r *http.Request, claims *toke archived = &valueArchived } + completedStr := r.URL.Query().Get("completed") + + var completed *bool + + if completedStr != "" { + valueCompleted, err := strconv.ParseBool(completedStr) + if err != nil { + return l.HTTPResponseErrorAction(logutils.ActionGet, model.TypeSurvey, nil, err, http.StatusInternalServerError, true) + } + completed = &valueCompleted + } + surveys, surverysRsponse, err := h.app.Client.GetSurveys(claims.OrgID, claims.AppID, nil, surveyIDs, surveyTypes, calendarEventID, - &limit, &offset, filter, public, archived) + &limit, &offset, filter, public, archived, completed) if err != nil { return l.HTTPResponseErrorAction(logutils.ActionGet, model.TypeSurvey, nil, err, http.StatusInternalServerError, true) } - resData := getSurveysResData(surveys, surverysRsponse) + resData := getSurveysResData(surveys, surverysRsponse, completed) sort.Slice(resData, func(i, j int) bool { return resData[i].DateCreated.After(resData[j].DateCreated) }) @@ -511,7 +523,7 @@ func (h ClientAPIsHandler) getCreatorSurveys(l *logs.Log, r *http.Request, claim offset = intParsed } - resData, _, err := h.app.Client.GetSurveys(claims.OrgID, claims.AppID, &claims.Subject, surveyIDs, surveyTypes, "", &limit, &offset, nil, nil, nil) + resData, _, err := h.app.Client.GetSurveys(claims.OrgID, claims.AppID, &claims.Subject, surveyIDs, surveyTypes, "", &limit, &offset, nil, nil, nil, nil) if err != nil { return l.HTTPResponseErrorAction(logutils.ActionGet, model.TypeSurvey, nil, err, http.StatusInternalServerError, true) } diff --git a/driver/web/convertions_surveys.go b/driver/web/convertions_surveys.go index 128138a..b75c3f9 100644 --- a/driver/web/convertions_surveys.go +++ b/driver/web/convertions_surveys.go @@ -97,31 +97,51 @@ func surveyTimeFilter(item *model.SurveyTimeFilterRequest) *model.SurveyTimeFilt EndTimeBefore: filter.EndTimeBefore} } -func getSurveyResData(item model.Survey, surveyResponse model.SurveyResponse) model.SurveysResponseData { - var complete bool +func getSurveysResData(items []model.Survey, surveyResponses []model.SurveyResponse, completed *bool) []model.SurveysResponseData { + var list []model.SurveysResponseData - if item.CreatorID == surveyResponse.UserID && item.AppID == surveyResponse.AppID && item.OrgID == surveyResponse.OrgID && item.ID == surveyResponse.Survey.ID { - complete = true - } else { - complete = false - } + for _, item := range items { + var isCompleted bool - return model.SurveysResponseData{ID: item.ID, CreatorID: item.CreatorID, OrgID: item.OrgID, AppID: item.AppID, Type: item.Type, Title: item.Title, - MoreInfo: item.MoreInfo, Data: item.Data, Scored: item.Scored, ResultRules: item.ResultRules, ResultJSON: item.ResultJSON, - SurveyStats: item.SurveyStats, Sensitive: item.Sensitive, Anonymous: item.Anonymous, DefaultDataKey: item.DefaultDataKey, - DefaultDataKeyRule: item.DefaultDataKeyRule, Constants: item.Constants, Strings: item.Strings, SubRules: item.SubRules, - ResponseKeys: item.ResponseKeys, CalendarEventID: item.CalendarEventID, StartDate: item.StartDate, EndDate: item.EndDate, - Public: item.Public, Archived: item.Archived, EstimatedCompletionTime: item.EstimatedCompletionTime, Complete: &complete} -} + for _, surveyResponse := range surveyResponses { + if item.ID == surveyResponse.Survey.ID { + isCompleted = true + break + } + } -func getSurveysResData(items []model.Survey, surveyResponses []model.SurveyResponse) []model.SurveysResponseData { - list := make([]model.SurveysResponseData, len(items)) - for index := range items { - var surveyResponse model.SurveyResponse - if index < len(surveyResponses) { - surveyResponse = surveyResponses[index] + if completed == nil || *completed == isCompleted { + list = append(list, model.SurveysResponseData{ + ID: item.ID, + CreatorID: item.CreatorID, + OrgID: item.OrgID, + AppID: item.AppID, + Type: item.Type, + Title: item.Title, + MoreInfo: item.MoreInfo, + Data: item.Data, + Scored: item.Scored, + ResultRules: item.ResultRules, + ResultJSON: item.ResultJSON, + SurveyStats: item.SurveyStats, + Sensitive: item.Sensitive, + Anonymous: item.Anonymous, + DefaultDataKey: item.DefaultDataKey, + DefaultDataKeyRule: item.DefaultDataKeyRule, + Constants: item.Constants, + Strings: item.Strings, + SubRules: item.SubRules, + ResponseKeys: item.ResponseKeys, + CalendarEventID: item.CalendarEventID, + StartDate: item.StartDate, + EndDate: item.EndDate, + Public: item.Public, + Archived: item.Archived, + EstimatedCompletionTime: item.EstimatedCompletionTime, + Completed: &isCompleted, + }) } - list[index] = getSurveyResData(items[index], surveyResponse) } + return list } diff --git a/driver/web/docs/gen/def.yaml b/driver/web/docs/gen/def.yaml index 9fc254f..ae05c03 100644 --- a/driver/web/docs/gen/def.yaml +++ b/driver/web/docs/gen/def.yaml @@ -2,7 +2,7 @@ openapi: 3.0.3 info: title: Rokwire Surveys Building Block API description: Surveys Building Block API Documentation - version: 1.8.2 + version: 1.9.0 servers: - url: 'https://api.rokwire.illinois.edu/surveys' description: Production server @@ -108,6 +108,14 @@ paths: explode: false schema: type: boolean + - name: completed + in: query + description: Shows if the survery is completed or not + required: false + style: simple + explode: false + schema: + type: boolean requestBody: description: Get survey time filter request body required: false @@ -975,6 +983,14 @@ paths: explode: false schema: type: boolean + - name: completed + in: query + description: Shows if the survery is completed or not + required: false + style: simple + explode: false + schema: + type: boolean requestBody: description: Get survey time filter request body required: false diff --git a/driver/web/docs/index.yaml b/driver/web/docs/index.yaml index 8a12a14..abfc26c 100644 --- a/driver/web/docs/index.yaml +++ b/driver/web/docs/index.yaml @@ -2,7 +2,7 @@ openapi: 3.0.3 info: title: Rokwire Surveys Building Block API description: Surveys Building Block API Documentation - version: 1.8.2 + version: 1.9.0 servers: - url: 'https://api.rokwire.illinois.edu/surveys' description: Production server diff --git a/driver/web/docs/resources/admin/surveys.yaml b/driver/web/docs/resources/admin/surveys.yaml index c3d19aa..793bdbd 100644 --- a/driver/web/docs/resources/admin/surveys.yaml +++ b/driver/web/docs/resources/admin/surveys.yaml @@ -63,7 +63,15 @@ get: style: simple explode: false schema: - type: boolean + type: boolean + - name: completed + in: query + description: Shows if the survery is completed or not + required: false + style: simple + explode: false + schema: + type: boolean requestBody: description: Get survey time filter request body required: false diff --git a/driver/web/docs/resources/client/surveys.yaml b/driver/web/docs/resources/client/surveys.yaml index c1c8cb4..b5e4503 100644 --- a/driver/web/docs/resources/client/surveys.yaml +++ b/driver/web/docs/resources/client/surveys.yaml @@ -61,7 +61,15 @@ get: style: simple explode: false schema: - type: boolean + type: boolean + - name: completed + in: query + description: Shows if the survery is completed or not + required: false + style: simple + explode: false + schema: + type: boolean requestBody: description: Get survey time filter request body required: false