diff --git a/cypress/e2e/03_features.cy.ts b/cypress/e2e/03_features.cy.ts
new file mode 100644
index 000000000..dd2c66424
--- /dev/null
+++ b/cypress/e2e/03_features.cy.ts
@@ -0,0 +1,158 @@
+import { User, HostName, Workspaces, Repositories, Features } from '../support/objects/objects';
+
+
+
+describe('Create Features for Workspace', () => {
+ it('passes', () => {
+ cy.upsertlogin(User).then(value => {
+ for(let i = 0; i <= 2; i++) {
+ cy.request({
+ method: 'POST',
+ url: `${HostName}/features`,
+ headers: { 'x-jwt': `${value}` },
+ body: Features[i]
+ }).its('body').then(body => {
+ expect(body).to.have.property('name').and.equal(Features[i].name.trim());
+ expect(body).to.have.property('brief').and.equal(Features[i].brief.trim());
+ expect(body).to.have.property('requirements').and.equal(Features[i].requirements.trim());
+ expect(body).to.have.property('architecture').and.equal(Features[i].architecture.trim());
+ });
+ }
+ })
+ })
+})
+
+describe('Modify name for Feature', () => {
+ it('passes', () => {
+ cy.upsertlogin(User).then(value => {
+ for(let i = 0; i <= 2; i++) {
+ cy.request({
+ method: 'POST',
+ url: `${HostName}/features`,
+ headers: { 'x-jwt': `${value}` },
+ body: {
+ uuid: Features[i].uuid,
+ name: Features[i].name + "_addtext"
+ }
+ }).its('body').then(body => {
+ expect(body).to.have.property('name').and.equal(Features[i].name.trim() + " _addtext");
+ expect(body).to.have.property('brief').and.equal(Features[i].brief.trim());
+ expect(body).to.have.property('requirements').and.equal(Features[i].requirements.trim());
+ expect(body).to.have.property('architecture').and.equal(Features[i].architecture.trim());
+ });
+ }
+ })
+ })
+})
+
+describe('Modify brief for Feature', () => {
+ it('passes', () => {
+ cy.upsertlogin(User).then(value => {
+ for(let i = 0; i <= 2; i++) {
+ cy.request({
+ method: 'POST',
+ url: `${HostName}/features`,
+ headers: { 'x-jwt': `${value}` },
+ body: {
+ uuid: Features[i].uuid,
+ brief: Features[i].brief + "_addtext"
+ }
+ }).its('body').then(body => {
+ expect(body).to.have.property('name').and.equal(Features[i].name.trim() + " _addtext");
+ expect(body).to.have.property('brief').and.equal(Features[i].brief.trim() + " _addtext");
+ expect(body).to.have.property('requirements').and.equal(Features[i].requirements.trim());
+ expect(body).to.have.property('architecture').and.equal(Features[i].architecture.trim());
+ });
+ }
+ })
+ })
+})
+
+describe('Modify requirements for Feature', () => {
+ it('passes', () => {
+ cy.upsertlogin(User).then(value => {
+ for(let i = 0; i <= 2; i++) {
+ cy.request({
+ method: 'POST',
+ url: `${HostName}/features`,
+ headers: { 'x-jwt': `${value}` },
+ body: {
+ uuid: Features[i].uuid,
+ requirements: Features[i].requirements + "_addtext"
+ }
+ }).its('body').then(body => {
+ expect(body).to.have.property('name').and.equal(Features[i].name.trim() + " _addtext");
+ expect(body).to.have.property('brief').and.equal(Features[i].brief.trim() + " _addtext");
+ expect(body).to.have.property('requirements').and.equal(Features[i].requirements.trim() + " _addtext");
+ expect(body).to.have.property('architecture').and.equal(Features[i].architecture.trim());
+ });
+ }
+ })
+ })
+})
+
+describe('Modify architecture for Feature', () => {
+ it('passes', () => {
+ cy.upsertlogin(User).then(value => {
+ for(let i = 0; i <= 2; i++) {
+ cy.request({
+ method: 'POST',
+ url: `${HostName}/features`,
+ headers: { 'x-jwt': `${value}` },
+ body: {
+ uuid: Features[i].uuid,
+ architecture: Features[i].architecture + "_addtext"
+ }
+ }).its('body').then(body => {
+ expect(body).to.have.property('name').and.equal(Features[i].name.trim() + " _addtext");
+ expect(body).to.have.property('brief').and.equal(Features[i].brief.trim() + " _addtext");
+ expect(body).to.have.property('requirements').and.equal(Features[i].requirements.trim() + " _addtext");
+ expect(body).to.have.property('architecture').and.equal(Features[i].architecture.trim() + " _addtext");
+ });
+ }
+ })
+ })
+})
+
+
+describe('Get Features for Workspace', () => {
+ it('passes', () => {
+ cy.upsertlogin(User).then(value => {
+ cy.request({
+ method: 'GET',
+ url: `${HostName}/features/forworkspace/` + Features[0].workspace_uuid,
+ headers: { 'x-jwt': `${ value }` },
+ body: {}
+ }).then((resp) => {
+ expect(resp.status).to.eq(200)
+ for(let i = 0; i <= 2; i++) {
+ expect(resp.body[i]).to.have.property('name', Features[i].name.trim() + " _addtext")
+ expect(resp.body[i]).to.have.property('brief', Features[i].brief.trim() + " _addtext")
+ expect(resp.body[i]).to.have.property('requirements', Features[i].requirements.trim() + " _addtext")
+ expect(resp.body[i]).to.have.property('architecture', Features[i].architecture.trim() + " _addtext")
+ }
+ })
+ })
+ })
+})
+
+describe('Get Feature by uuid', () => {
+ it('passes', () => {
+ cy.upsertlogin(User).then(value => {
+ for(let i = 0; i <= 2; i++) {
+ cy.request({
+ method: 'GET',
+ url: `${HostName}/features/` + Features[i].uuid,
+ headers: { 'x-jwt': `${ value }` },
+ body: {}
+ }).then((resp) => {
+ expect(resp.status).to.eq(200)
+ expect(resp.body).to.have.property('name', Features[i].name.trim() + " _addtext")
+ expect(resp.body).to.have.property('brief', Features[i].brief.trim() + " _addtext")
+ expect(resp.body).to.have.property('requirements', Features[i].requirements.trim() + " _addtext")
+ expect(resp.body).to.have.property('architecture', Features[i].architecture.trim() + " _addtext")
+ })
+ }
+ })
+ })
+})
diff --git a/cypress/support/objects/objects.ts b/cypress/support/objects/objects.ts
index e1a2f7328..9c1908ca8 100644
--- a/cypress/support/objects/objects.ts
+++ b/cypress/support/objects/objects.ts
@@ -74,7 +74,7 @@ export const Features = [
{
uuid: 'com1kson1e49th88dbg0',
workspace_uuid: 'cohob00n1e4808utqel0',
- name: 'Hive Process',
+ name: ' Hive Process ',
priority: 1,
brief: ' To follow a set of best practices in product development.' +
'Dividing complex features into small
steps makes it easier to ' +
@@ -100,7 +100,7 @@ export const Features = [
' the base from which every technical decition relays on.
' +
'We are going to leverage AI to help the PM write better definitions.
' +
'The fields that would benefit form AI assistance are: mission, tactics, ' +
- 'feature brief and feature user stories',
+ 'feature brief and feature user stories ',
requirements: ' Create a new page for a conversation format between the PM and the LLM
' +
'Rely as much as possible on stakwork workflows
' +
'Have history of previous definitions ',
@@ -110,7 +110,7 @@ export const Features = [
'Front
',
},
{
- uuid: 'com1l5on1e49tucv350g',
+ uuid: 'com1l5on1e49tucv350h',
workspace_uuid: 'cohob00n1e4808utqel0',
name: ' AI Assited relation between text fields ',
priority: 2,
@@ -118,9 +118,9 @@ export const Features = [
'feature brief, user stories, requirements and architecture should have some ' +
'relation between each other.
' + 'One way to do that is to leverage an LLM ' +
'to discern the parts of the defintion that have a connection to other definitions.
' +
- 'The UI will need to show the user how each definition is related to other defintions.',
+ 'The UI will need to show the user how each definition is related to other defintions. ',
requirements: 'Create a new process after a Feature text has changed. It should use the LLM to ' +
- 'determine de relationship between parts of the text.',
+ 'determine de relationship between parts of the text. ',
architecture: 'Describe the architecture of the feature with the following sections:' +
'
Wireframes
Visual Schematics
Object Definition
' +
'DB Schema Changes
UX
CI/CD
Changes
Endpoints
' +
diff --git a/db/config.go b/db/config.go
index 547ca97a1..3ffc2c3a1 100644
--- a/db/config.go
+++ b/db/config.go
@@ -68,6 +68,7 @@ func InitDB() {
db.AutoMigrate(&BountyRoles{})
db.AutoMigrate(&UserInvoiceData{})
db.AutoMigrate(&WorkspaceRepositories{})
+ db.AutoMigrate(&WorkspaceFeatures{})
DB.MigrateTablesWithOrgUuid()
DB.MigrateOrganizationToWorkspace()
@@ -178,6 +179,8 @@ func (db database) MigrateTablesWithOrgUuid() {
if !db.db.Migrator().HasTable("bounty") {
if !db.db.Migrator().HasColumn(Bounty{}, "workspace_uuid") {
db.db.AutoMigrate(&Bounty{})
+ } else {
+ db.db.AutoMigrate(&NewBounty{})
}
}
if !db.db.Migrator().HasTable("budget_histories") {
@@ -188,16 +191,22 @@ func (db database) MigrateTablesWithOrgUuid() {
if !db.db.Migrator().HasTable("payment_histories") {
if !db.db.Migrator().HasColumn(PaymentHistory{}, "workspace_uuid") {
db.db.AutoMigrate(&PaymentHistory{})
+ } else {
+ db.db.AutoMigrate(&NewPaymentHistory{})
}
}
if !db.db.Migrator().HasTable("invoice_list") {
if !db.db.Migrator().HasColumn(InvoiceList{}, "workspace_uuid") {
db.db.AutoMigrate(&InvoiceList{})
+ } else {
+ db.db.AutoMigrate(&NewInvoiceList{})
}
}
if !db.db.Migrator().HasTable("bounty_budgets") {
if !db.db.Migrator().HasColumn(BountyBudget{}, "workspace_uuid") {
db.db.AutoMigrate(&BountyBudget{})
+ } else {
+ db.db.AutoMigrate(&NewBountyBudget{})
}
}
if !db.db.Migrator().HasTable("workspace_user_roles") {
diff --git a/db/db.go b/db/db.go
index d2af69fb5..ed1fd7e18 100644
--- a/db/db.go
+++ b/db/db.go
@@ -1142,7 +1142,7 @@ func (db database) GetAllBounties(r *http.Request) []NewBounty {
languageArray := strings.Split(languages, ",")
languageLength := len(languageArray)
- if workspaceUuid != "" && orgUuid != "" {
+ if workspaceUuid == "" && orgUuid != "" {
workspaceUuid = orgUuid
}
diff --git a/db/features.go b/db/features.go
new file mode 100644
index 000000000..c978124be
--- /dev/null
+++ b/db/features.go
@@ -0,0 +1,41 @@
+package db
+
+import (
+ "strings"
+ "time"
+)
+
+func (db database) GetFeaturesByWorkspaceUuid(uuid string) []WorkspaceFeatures {
+ ms := []WorkspaceFeatures{}
+
+ db.db.Model(&WorkspaceFeatures{}).Where("workspace_uuid = ?", uuid).Order("Created").Find(&ms)
+
+ return ms
+}
+
+func (db database) GetFeatureByUuid(uuid string) WorkspaceFeatures {
+ ms := WorkspaceFeatures{}
+
+ db.db.Model(&WorkspaceFeatures{}).Where("uuid = ?", uuid).Find(&ms)
+
+ return ms
+}
+
+func (db database) CreateOrEditFeature(m WorkspaceFeatures) (WorkspaceFeatures, error) {
+ m.Name = strings.TrimSpace(m.Name)
+ m.Brief = strings.TrimSpace(m.Brief)
+ m.Requirements = strings.TrimSpace(m.Requirements)
+ m.Architecture = strings.TrimSpace(m.Architecture)
+
+ now := time.Now()
+ m.Updated = &now
+
+ if db.db.Model(&m).Where("uuid = ?", m.Uuid).Updates(&m).RowsAffected == 0 {
+ m.Created = &now
+ db.db.Create(&m)
+ }
+
+ db.db.Model(&WorkspaceFeatures{}).Where("uuid = ?", m.Uuid).Find(&m)
+
+ return m, nil
+}
diff --git a/db/interface.go b/db/interface.go
index 691c737dd..a5c27c430 100644
--- a/db/interface.go
+++ b/db/interface.go
@@ -122,18 +122,19 @@ type Database interface {
DeleteAllUsersFromWorkspace(uuid string) error
GetFilterStatusCount() FilterStattuCount
UserHasManageBountyRoles(pubKeyFromAuth string, uuid string) bool
- BountiesPaidPercentage(r PaymentDateRange) uint
- TotalSatsPosted(r PaymentDateRange) uint
- TotalSatsPaid(r PaymentDateRange) uint
- SatsPaidPercentage(r PaymentDateRange) uint
- AveragePaidTime(r PaymentDateRange) uint
- AverageCompletedTime(r PaymentDateRange) uint
- TotalBountiesPosted(r PaymentDateRange) int64
- TotalPaidBounties(r PaymentDateRange) int64
- NewHuntersPaid(r PaymentDateRange) int64
- TotalHuntersPaid(r PaymentDateRange) int64
+ BountiesPaidPercentage(r PaymentDateRange, workspace string) uint
+ TotalSatsPosted(r PaymentDateRange, workspace string) uint
+ TotalSatsPaid(r PaymentDateRange, workspace string) uint
+ SatsPaidPercentage(r PaymentDateRange, workspace string) uint
+ AveragePaidTime(r PaymentDateRange, workspace string) uint
+ AverageCompletedTime(r PaymentDateRange, workspace string) uint
+ TotalBountiesPosted(r PaymentDateRange, workspace string) int64
+ TotalPaidBounties(r PaymentDateRange, workspace string) int64
+ TotalAssignedBounties(r PaymentDateRange, workspace string) int64
+ NewHuntersPaid(r PaymentDateRange, workspace string) int64
+ TotalHuntersPaid(r PaymentDateRange, workspace string) int64
GetPersonByPubkey(pubkey string) Person
- GetBountiesByDateRange(r PaymentDateRange, re *http.Request) []Bounty
+ GetBountiesByDateRange(r PaymentDateRange, re *http.Request) []NewBounty
GetBountiesByDateRangeCount(r PaymentDateRange, re *http.Request) int64
GetBountiesProviders(r PaymentDateRange, re *http.Request) []Person
PersonUniqueNameFromName(name string) (string, error)
@@ -141,4 +142,7 @@ type Database interface {
UserHasAccess(pubKeyFromAuth string, uuid string, role string) bool
CreateWorkspaceRepository(m WorkspaceRepositories) (WorkspaceRepositories, error)
GetWorkspaceRepositorByWorkspaceUuid(uuid string) []WorkspaceRepositories
+ CreateOrEditFeature(m WorkspaceFeatures) (WorkspaceFeatures, error)
+ GetFeaturesByWorkspaceUuid(uuid string) []WorkspaceFeatures
+ GetFeatureByUuid(uuid string) WorkspaceFeatures
}
diff --git a/db/metrics.go b/db/metrics.go
index 1a1f0e870..0fd676c2b 100644
--- a/db/metrics.go
+++ b/db/metrics.go
@@ -23,27 +23,45 @@ func (db database) TotalWorkspacesByDateRange(r PaymentDateRange) int64 {
return count
}
-func (db database) TotalPaymentsByDateRange(r PaymentDateRange) uint {
+func (db database) TotalPaymentsByDateRange(r PaymentDateRange, workspace string) uint {
var sum uint
- db.db.Model(&PaymentHistory{}).Where("payment_type = ?", r.PaymentType).Where("created >= ?", r.StartDate).Where("created <= ?", r.EndDate).Select("SUM(amount)").Row().Scan(&sum)
+ query := db.db.Model(&NewPaymentHistory{}).Where("payment_type = ?", r.PaymentType).Where("created >= ?", r.StartDate).Where("created <= ?", r.EndDate)
+
+ if workspace != "" {
+ query.Where("workspace_uuid", workspace)
+ }
+
+ query.Select("SUM(amount)").Row().Scan(&sum)
return sum
}
-func (db database) TotalSatsPosted(r PaymentDateRange) uint {
+func (db database) TotalSatsPosted(r PaymentDateRange, workspace string) uint {
var sum uint
- db.db.Model(&Bounty{}).Where("created >= ?", r.StartDate).Where("created <= ?", r.EndDate).Select("SUM(price)").Row().Scan(&sum)
+ query := db.db.Model(&NewBounty{}).Where("created >= ?", r.StartDate).Where("created <= ?", r.EndDate)
+
+ if workspace != "" {
+ query.Where("workspace_uuid", workspace)
+ }
+
+ query.Select("SUM(price)").Row().Scan(&sum)
return sum
}
-func (db database) TotalSatsPaid(r PaymentDateRange) uint {
+func (db database) TotalSatsPaid(r PaymentDateRange, workspace string) uint {
var sum uint
- db.db.Model(&Bounty{}).Where("paid = ?", true).Where("created >= ?", r.StartDate).Where("created <= ?", r.EndDate).Select("SUM(price)").Row().Scan(&sum)
+ query := db.db.Model(&NewBounty{}).Where("paid = ?", true).Where("created >= ?", r.StartDate).Where("created <= ?", r.EndDate)
+
+ if workspace != "" {
+ query.Where("workspace_uuid", workspace)
+ }
+
+ query.Select("SUM(price)").Row().Scan(&sum)
return sum
}
-func (db database) SatsPaidPercentage(r PaymentDateRange) uint {
- satsPosted := DB.TotalSatsPosted(r)
- satsPaid := DB.TotalSatsPaid(r)
+func (db database) SatsPaidPercentage(r PaymentDateRange, workspace string) uint {
+ satsPosted := DB.TotalSatsPosted(r, workspace)
+ satsPaid := DB.TotalSatsPaid(r, workspace)
if satsPaid != 0 && satsPosted != 0 {
value := (satsPaid * 100) / satsPosted
paidPercentage := math.Round(float64(value))
@@ -52,43 +70,81 @@ func (db database) SatsPaidPercentage(r PaymentDateRange) uint {
return 0
}
-func (db database) TotalPaidBounties(r PaymentDateRange) int64 {
+func (db database) TotalPaidBounties(r PaymentDateRange, workspace string) int64 {
+ var count int64
+ query := db.db.Model(&NewBounty{}).Where("paid = ?", true).Where("created >= ?", r.StartDate).Where("created <= ?", r.EndDate)
+
+ if workspace != "" {
+ query.Where("workspace_uuid", workspace)
+ }
+
+ query.Count(&count)
+ return count
+}
+
+func (db database) TotalAssignedBounties(r PaymentDateRange, workspace string) int64 {
var count int64
- db.db.Model(&Bounty{}).Where("paid = ?", true).Where("created >= ?", r.StartDate).Where("created <= ?", r.EndDate).Count(&count)
+ query := db.db.Model(&NewBounty{}).Where("assignee != ''").Where("paid = ?", false).Where("created >= ?", r.StartDate).Where("created <= ?", r.EndDate)
+
+ if workspace != "" {
+ query.Where("workspace_uuid", workspace)
+ }
+
+ query.Count(&count)
return count
}
-func (db database) TotalHuntersPaid(r PaymentDateRange) int64 {
+func (db database) TotalHuntersPaid(r PaymentDateRange, workspace string) int64 {
var count int64
query := fmt.Sprintf(`SELECT COUNT(DISTINCT assignee) FROM bounty WHERE assignee !='' AND paid=true AND created >= %s AND created <= %s`, r.StartDate, r.EndDate)
- db.db.Raw(query).Count(&count)
+ var workspaceQuery string
+ if workspace != "" {
+ workspaceQuery = fmt.Sprintf("AND workspace_uuid = '%s'", workspace)
+ }
+
+ allQuery := query + " " + workspaceQuery
+ db.db.Raw(allQuery).Count(&count)
return count
}
-func (db database) NewHuntersPaid(r PaymentDateRange) int64 {
+func (db database) NewHuntersPaid(r PaymentDateRange, workspace string) int64 {
var count int64
- db.db.Model(&Bounty{}).
+
+ query := db.db.Model(&NewBounty{}).
Select("DISTINCT assignee").
Where("paid = true").
- Where("created >= ?", r.StartDate).Where("created <= ?", r.EndDate).
- Not("assignee IN (?)", db.db.Model(&Bounty{}).
- Select("assignee").
- Where("paid = true").
- Where("created < ?", r.StartDate),
- ).Count(&count)
+ Where("created >= ?", r.StartDate).Where("created <= ?", r.EndDate)
+
+ if workspace != "" {
+ query.Where("workspace_uuid", workspace)
+ }
+
+ query.Not("assignee IN (?)", db.db.Model(&NewBounty{}).
+ Select("assignee").
+ Where("paid = true").
+ Where("created < ?", r.StartDate),
+ )
+
+ query.Count(&count)
return count
}
-func (db database) TotalBountiesPosted(r PaymentDateRange) int64 {
+func (db database) TotalBountiesPosted(r PaymentDateRange, workspace string) int64 {
var count int64
- db.db.Model(&Bounty{}).Where("created >= ?", r.StartDate).Where("created <= ?", r.EndDate).Count(&count)
+ query := db.db.Model(&Bounty{}).Where("created >= ?", r.StartDate).Where("created <= ?", r.EndDate)
+
+ if workspace != "" {
+ query.Where("workspace_uuid", workspace)
+ }
+
+ query.Count(&count)
return count
}
-func (db database) BountiesPaidPercentage(r PaymentDateRange) uint {
- bountiesPosted := DB.TotalBountiesPosted(r)
- bountiesPaid := DB.TotalPaidBounties(r)
+func (db database) BountiesPaidPercentage(r PaymentDateRange, workspace string) uint {
+ bountiesPosted := DB.TotalBountiesPosted(r, workspace)
+ bountiesPaid := DB.TotalPaidBounties(r, workspace)
if bountiesPaid != 0 && bountiesPosted != 0 {
value := bountiesPaid * 100 / bountiesPosted
paidPercentage := math.Round(float64(value))
@@ -97,23 +153,31 @@ func (db database) BountiesPaidPercentage(r PaymentDateRange) uint {
return 0
}
-func (db database) PaidDifference(r PaymentDateRange) []DateDifference {
+func (db database) PaidDifference(r PaymentDateRange, workspace string) []DateDifference {
ms := []DateDifference{}
- db.db.Raw(`SELECT EXTRACT(EPOCH FROM (paid_date - TO_TIMESTAMP(created))) as diff FROM public.bounty WHERE paid_date IS NOT NULL AND created >= '` + r.StartDate + `' AND created <= '` + r.EndDate + `' `).Find(&ms)
+ query := fmt.Sprintf("SELECT EXTRACT(EPOCH FROM (paid_date - TO_TIMESTAMP(created))) as diff FROM public.bounty WHERE paid_date IS NOT NULL AND created >= %s AND created <= %s", r.StartDate, r.EndDate)
+
+ var workspaceQuery string
+ if workspace != "" {
+ workspaceQuery = fmt.Sprintf("AND workspace_uuid = '%s'", workspace)
+ }
+
+ allQuery := query + " " + workspaceQuery
+ db.db.Raw(allQuery).Find(&ms)
return ms
}
-func (db database) PaidDifferenceCount(r PaymentDateRange) int64 {
+func (db database) PaidDifferenceCount(r PaymentDateRange, workspace string) int64 {
var count int64
- list := db.PaidDifference(r)
+ list := db.PaidDifference(r, workspace)
count = int64(len(list))
return count
}
-func (db database) AveragePaidTime(r PaymentDateRange) uint {
- paidList := DB.PaidDifference(r)
- paidCount := DB.PaidDifferenceCount(r)
+func (db database) AveragePaidTime(r PaymentDateRange, workspace string) uint {
+ paidList := DB.PaidDifference(r, workspace)
+ paidCount := DB.PaidDifferenceCount(r, workspace)
var paidSum uint
for _, diff := range paidList {
paidSum = uint(math.Round(diff.Diff))
@@ -121,23 +185,31 @@ func (db database) AveragePaidTime(r PaymentDateRange) uint {
return CalculateAverageDays(paidCount, paidSum)
}
-func (db database) CompletedDifference(r PaymentDateRange) []DateDifference {
+func (db database) CompletedDifference(r PaymentDateRange, workspace string) []DateDifference {
ms := []DateDifference{}
- db.db.Raw(`SELECT EXTRACT(EPOCH FROM (completion_date - TO_TIMESTAMP(created))) as diff FROM public.bounty WHERE completion_date IS NOT NULL AND created >= '` + r.StartDate + `' AND created <= '` + r.EndDate + `' `).Find(&ms)
+ query := fmt.Sprintf("SELECT EXTRACT(EPOCH FROM (completion_date - TO_TIMESTAMP(created))) as diff FROM public.bounty WHERE completion_date IS NOT NULL AND created >= %s AND created <= %s", r.StartDate, r.EndDate)
+
+ var workspaceQuery string
+ if workspace != "" {
+ workspaceQuery = fmt.Sprintf("AND workspace_uuid = '%s'", workspace)
+ }
+
+ allQuery := query + " " + workspaceQuery
+ db.db.Raw(allQuery).Find(&ms)
return ms
}
-func (db database) CompletedDifferenceCount(r PaymentDateRange) int64 {
+func (db database) CompletedDifferenceCount(r PaymentDateRange, workspace string) int64 {
var count int64
- list := db.CompletedDifference(r)
+ list := db.CompletedDifference(r, workspace)
count = int64(len(list))
return count
}
-func (db database) AverageCompletedTime(r PaymentDateRange) uint {
- paidList := DB.CompletedDifference(r)
- paidCount := DB.CompletedDifferenceCount(r)
+func (db database) AverageCompletedTime(r PaymentDateRange, workspace string) uint {
+ paidList := DB.CompletedDifference(r, workspace)
+ paidCount := DB.CompletedDifferenceCount(r, workspace)
var paidSum uint
for _, diff := range paidList {
paidSum = uint(math.Round(diff.Diff))
@@ -155,16 +227,18 @@ func CalculateAverageDays(paidCount int64, paidSum uint) uint {
return 0
}
-func (db database) GetBountiesByDateRange(r PaymentDateRange, re *http.Request) []Bounty {
+func (db database) GetBountiesByDateRange(r PaymentDateRange, re *http.Request) []NewBounty {
offset, limit, sortBy, direction, _ := utils.GetPaginationParams(re)
keys := re.URL.Query()
open := keys.Get("Open")
assingned := keys.Get("Assigned")
paid := keys.Get("Paid")
providers := keys.Get("provider")
+ workspace := keys.Get("workspace")
orderQuery := ""
limitQuery := ""
+ workspaceQuery := ""
var statusConditions []string
@@ -190,9 +264,12 @@ func (db database) GetBountiesByDateRange(r PaymentDateRange, re *http.Request)
} else {
orderQuery = " ORDER BY " + sortBy + "" + "DESC"
}
- if limit > 0 {
+ if limit > 1 {
limitQuery = fmt.Sprintf("LIMIT %d OFFSET %d", limit, offset)
}
+ if workspace != "" {
+ workspaceQuery = fmt.Sprintf("AND workspace_uuid = '%s'", workspace)
+ }
providerCondition := ""
if len(providers) > 0 {
@@ -201,10 +278,11 @@ func (db database) GetBountiesByDateRange(r PaymentDateRange, re *http.Request)
}
query := `SELECT * FROM public.bounty WHERE created >= '` + r.StartDate + `' AND created <= '` + r.EndDate + `'` + providerCondition
- allQuery := query + " " + statusQuery + " " + orderQuery + " " + limitQuery
+ allQuery := query + " " + workspaceQuery + " " + statusQuery + " " + orderQuery + " " + limitQuery
- b := []Bounty{}
+ b := []NewBounty{}
db.db.Raw(allQuery).Find(&b)
+
return b
}
@@ -214,6 +292,7 @@ func (db database) GetBountiesByDateRangeCount(r PaymentDateRange, re *http.Requ
assingned := keys.Get("Assigned")
paid := keys.Get("Paid")
providers := keys.Get("provider")
+ workspace := keys.Get("workspace")
var statusConditions []string
@@ -239,11 +318,15 @@ func (db database) GetBountiesByDateRangeCount(r PaymentDateRange, re *http.Requ
providerSlice := strings.Split(providers, ",")
providerCondition = " AND owner_id IN ('" + strings.Join(providerSlice, "','") + "')"
}
+ var workspaceQuery string
+ if workspace != "" {
+ workspaceQuery = fmt.Sprintf("AND workspace_uuid = '%s'", workspace)
+ }
var count int64
query := `SELECT COUNT(*) FROM public.bounty WHERE created >= '` + r.StartDate + `' AND created <= '` + r.EndDate + `'` + providerCondition
- allQuery := query + " " + statusQuery
+ allQuery := query + " " + workspaceQuery + " " + statusQuery
db.db.Raw(allQuery).Scan(&count)
return count
}
diff --git a/db/structs.go b/db/structs.go
index c4aba003e..adf07ed70 100644
--- a/db/structs.go
+++ b/db/structs.go
@@ -429,38 +429,42 @@ type BountyOwners struct {
}
type BountyData struct {
- Bounty
+ NewBounty
BountyId uint `json:"bounty_id"`
BountyCreated int64 `json:"bounty_created"`
BountyUpdated *time.Time `json:"bounty_updated"`
BountyDescription string `json:"bounty_description"`
Person
- AssigneeAlias string `json:"assignee_alias"`
- AssigneeId uint `json:"assignee_id"`
- AssigneeImg string `json:"assignee_img"`
- AssigneeCreated *time.Time `json:"assignee_created"`
- AssigneeUpdated *time.Time `json:"assignee_updated"`
- AssigneeDescription string `json:"assignee_description"`
- AssigneeRouteHint string `json:"assignee_route_hint"`
- BountyOwnerId uint `json:"bounty_owner_id"`
- OwnerUuid string `json:"owner_uuid"`
- OwnerKey string `json:"owner_key"`
- OwnerAlias string `json:"owner_alias"`
- OwnerUniqueName string `json:"owner_unique_name"`
- OwnerDescription string `json:"owner_description"`
- OwnerTags pq.StringArray `gorm:"type:text[]" json:"owner_tags" null`
- OwnerImg string `json:"owner_img"`
- OwnerCreated *time.Time `json:"owner_created"`
- OwnerUpdated *time.Time `json:"owner_updated"`
- OwnerLastLogin int64 `json:"owner_last_login"`
- OwnerRouteHint string `json:"owner_route_hint"`
- OwnerContactKey string `json:"owner_contact_key"`
- OwnerPriceToMeet int64 `json:"owner_price_to_meet"`
- OwnerTwitterConfirmed bool `json:"owner_twitter_confirmed"`
- OrganizationName string `json:"organization_name"`
- OrganizationImg string `json:"organization_img"`
- WorkspaceUuid string `json:"organization_uuid"`
- WorkspaceDescription string `json:"description"`
+ AssigneeAlias string `json:"assignee_alias"`
+ AssigneeId uint `json:"assignee_id"`
+ AssigneeImg string `json:"assignee_img"`
+ AssigneeCreated *time.Time `json:"assignee_created"`
+ AssigneeUpdated *time.Time `json:"assignee_updated"`
+ AssigneeDescription string `json:"assignee_description"`
+ AssigneeRouteHint string `json:"assignee_route_hint"`
+ BountyOwnerId uint `json:"bounty_owner_id"`
+ OwnerUuid string `json:"owner_uuid"`
+ OwnerKey string `json:"owner_key"`
+ OwnerAlias string `json:"owner_alias"`
+ OwnerUniqueName string `json:"owner_unique_name"`
+ OwnerDescription string `json:"owner_description"`
+ OwnerTags pq.StringArray `gorm:"type:text[]" json:"owner_tags" null`
+ OwnerImg string `json:"owner_img"`
+ OwnerCreated *time.Time `json:"owner_created"`
+ OwnerUpdated *time.Time `json:"owner_updated"`
+ OwnerLastLogin int64 `json:"owner_last_login"`
+ OwnerRouteHint string `json:"owner_route_hint"`
+ OwnerContactKey string `json:"owner_contact_key"`
+ OwnerPriceToMeet int64 `json:"owner_price_to_meet"`
+ OwnerTwitterConfirmed bool `json:"owner_twitter_confirmed"`
+ OrganizationName string `json:"organization_name"`
+ OrganizationImg string `json:"organization_img"`
+ OrganizationUuid string `json:"organization_uuid"`
+ OrganizationDescription string `json:"description"`
+ WorkspaceName string `json:"workspace_name"`
+ WorkspaceImg string `json:"workspace_img"`
+ WorkspaceUuid string `json:"workspace_uuid"`
+ WorkspaceDescription string `json:"workspace_description"`
}
type BountyResponse struct {
@@ -559,6 +563,20 @@ type WorkspaceRepositories struct {
UpdatedBy string `json:"updated_by"`
}
+type WorkspaceFeatures struct {
+ ID uint `json:"id"`
+ Uuid string `json:"uuid"`
+ WorkspaceUuid string `json:"workspace_uuid"`
+ Name string `json:"name"`
+ Brief string `json:"brief"`
+ Requirements string `json:"requirements"`
+ Architecture string `json:"architecture"`
+ Created *time.Time `json:"created"`
+ Updated *time.Time `json:"updated"`
+ CreatedBy string `json:"created_by"`
+ UpdatedBy string `json:"updated_by"`
+}
+
type BountyRoles struct {
Name string `json:"name"`
}
@@ -599,15 +617,18 @@ type NewBountyBudget struct {
}
type StatusBudget struct {
- OrgUuid string `json:"org_uuid"`
- WorkspaceUuid string `json:"workspace_uuid"`
- CurrentBudget uint `json:"current_budget"`
- OpenBudget uint `json:"open_budget"`
- OpenCount int64 `json:"open_count"`
- AssignedBudget uint `json:"assigned_budget"`
- AssignedCount int64 `json:"assigned_count"`
- CompletedBudget uint `json:"completed_budget"`
- CompletedCount int64 `json:"completed_count"`
+ OrgUuid string `json:"org_uuid"`
+ WorkspaceUuid string `json:"workspace_uuid"`
+ CurrentBudget uint `json:"current_budget"`
+ OpenBudget uint `json:"open_budget"`
+ OpenCount int64 `json:"open_count"`
+ OpenDifference int `json:"open_difference"`
+ AssignedBudget uint `json:"assigned_budget"`
+ AssignedCount int64 `json:"assigned_count"`
+ AssignedDifference int `json:"assigned_difference"`
+ CompletedBudget uint `json:"completed_budget"`
+ CompletedCount int64 `json:"completed_count"`
+ CompletedDifference int `json:"completed_difference"`
}
type BudgetInvoiceRequest struct {
@@ -811,6 +832,7 @@ type DateDifference struct {
type BountyMetrics struct {
BountiesPosted int64 `json:"bounties_posted"`
BountiesPaid int64 `json:"bounties_paid"`
+ BountiesAssigned int64 `json:"bounties_assigned"`
BountiesPaidPercentage uint `json:"bounties_paid_average"`
SatsPosted uint `json:"sats_posted"`
SatsPaid uint `json:"sats_paid"`
diff --git a/db/workspaces.go b/db/workspaces.go
index 406be7d01..0087d9b6e 100644
--- a/db/workspaces.go
+++ b/db/workspaces.go
@@ -14,8 +14,13 @@ func (db database) GetWorkspaces(r *http.Request) []Workspace {
ms := []Workspace{}
offset, limit, sortBy, direction, search := utils.GetPaginationParams(r)
- // return if like owner_alias, unique_name, or equals pubkey
- db.db.Offset(offset).Limit(limit).Order(sortBy+" "+direction+" ").Where("LOWER(name) LIKE ?", "%"+search+"%").Where("deleted != ?", false).Find(&ms)
+ query := db.db.Model(&ms).Where("LOWER(name) LIKE ?", "%"+search+"%").Where("deleted != ?", true)
+
+ if limit > 1 {
+ query.Offset(offset).Limit(limit).Order(sortBy + " " + direction + " ")
+ }
+
+ query.Find(&ms)
return ms
}
@@ -186,28 +191,37 @@ func (db database) GetWorkspaceStatusBudget(workspace_uuid string) StatusBudget
var openCount int64
db.db.Model(&Bounty{}).Where("assignee = '' ").Where("paid != true").Count(&openCount)
+ var openDifference int = int(orgBudget.TotalBudget - openBudget)
+
var assignedBudget uint
db.db.Model(&Bounty{}).Where("assignee != '' ").Where("paid != true").Select("SUM(price)").Row().Scan(&assignedBudget)
var assignedCount int64
db.db.Model(&Bounty{}).Where("assignee != '' ").Where("paid != true").Count(&assignedCount)
+ var assignedDifference int = int(orgBudget.TotalBudget - assignedBudget)
+
var completedBudget uint
db.db.Model(&Bounty{}).Where("completed = true ").Where("paid != true").Select("SUM(price)").Row().Scan(&completedBudget)
var completedCount int64
db.db.Model(&Bounty{}).Where("completed = true ").Where("paid != true").Count(&completedCount)
+ var completedDifference int = int(orgBudget.TotalBudget - completedBudget)
+
statusBudget := StatusBudget{
- OrgUuid: workspace_uuid,
- WorkspaceUuid: workspace_uuid,
- CurrentBudget: orgBudget.TotalBudget,
- OpenBudget: openBudget,
- OpenCount: openCount,
- AssignedBudget: assignedBudget,
- AssignedCount: assignedCount,
- CompletedBudget: completedBudget,
- CompletedCount: completedCount,
+ OrgUuid: workspace_uuid,
+ WorkspaceUuid: workspace_uuid,
+ CurrentBudget: orgBudget.TotalBudget,
+ OpenBudget: openBudget,
+ OpenCount: openCount,
+ OpenDifference: openDifference,
+ AssignedBudget: assignedBudget,
+ AssignedCount: assignedCount,
+ AssignedDifference: assignedDifference,
+ CompletedBudget: completedBudget,
+ CompletedCount: completedCount,
+ CompletedDifference: completedDifference,
}
return statusBudget
diff --git a/handlers/features.go b/handlers/features.go
new file mode 100644
index 000000000..9c4f9cd68
--- /dev/null
+++ b/handlers/features.go
@@ -0,0 +1,95 @@
+package handlers
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+
+ "github.com/go-chi/chi"
+ "github.com/stakwork/sphinx-tribes/auth"
+ "github.com/stakwork/sphinx-tribes/db"
+)
+
+type featureHandler struct {
+ db db.Database
+}
+
+func NewFeatureHandler(database db.Database) *featureHandler {
+ return &featureHandler{
+ db: database,
+ }
+}
+
+func (oh *featureHandler) CreateOrEditFeatures(w http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
+ pubKeyFromAuth, _ := ctx.Value(auth.ContextKey).(string)
+ if pubKeyFromAuth == "" {
+ fmt.Println("no pubkey from auth")
+ w.WriteHeader(http.StatusUnauthorized)
+ return
+ }
+
+ features := db.WorkspaceFeatures{}
+ body, _ := io.ReadAll(r.Body)
+ r.Body.Close()
+ err := json.Unmarshal(body, &features)
+
+ if err != nil {
+ fmt.Println(err)
+ w.WriteHeader(http.StatusNotAcceptable)
+ return
+ }
+
+ features.CreatedBy = pubKeyFromAuth
+
+ // Validate struct data
+ err = db.Validate.Struct(features)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ msg := fmt.Sprintf("Error: did not pass validation test : %s", err)
+ json.NewEncoder(w).Encode(msg)
+ return
+ }
+
+ p, err := oh.db.CreateOrEditFeature(features)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ return
+ }
+
+ w.WriteHeader(http.StatusOK)
+ json.NewEncoder(w).Encode(p)
+}
+
+func (oh *featureHandler) GetFeaturesByWorkspaceUuid(w http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
+ pubKeyFromAuth, _ := ctx.Value(auth.ContextKey).(string)
+ if pubKeyFromAuth == "" {
+ fmt.Println("no pubkey from auth")
+ w.WriteHeader(http.StatusUnauthorized)
+ return
+ }
+
+ uuid := chi.URLParam(r, "uuid")
+ workspaceFeatures := oh.db.GetFeaturesByWorkspaceUuid(uuid)
+
+ w.WriteHeader(http.StatusOK)
+ json.NewEncoder(w).Encode(workspaceFeatures)
+}
+
+func (oh *featureHandler) GetFeatureByUuid(w http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
+ pubKeyFromAuth, _ := ctx.Value(auth.ContextKey).(string)
+ if pubKeyFromAuth == "" {
+ fmt.Println("no pubkey from auth")
+ w.WriteHeader(http.StatusUnauthorized)
+ return
+ }
+
+ uuid := chi.URLParam(r, "uuid")
+ workspaceFeature := oh.db.GetFeatureByUuid(uuid)
+
+ w.WriteHeader(http.StatusOK)
+ json.NewEncoder(w).Encode(workspaceFeature)
+}
diff --git a/handlers/metrics.go b/handlers/metrics.go
index 2a0140cb5..27f1378b7 100644
--- a/handlers/metrics.go
+++ b/handlers/metrics.go
@@ -33,6 +33,8 @@ func NewMetricHandler(db db.Database) *metricHandler {
func PaymentMetrics(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
pubKeyFromAuth, _ := ctx.Value(auth.ContextKey).(string)
+ keys := r.URL.Query()
+ workspace := keys.Get("workspace")
if pubKeyFromAuth == "" {
fmt.Println("no pubkey from auth")
@@ -51,7 +53,7 @@ func PaymentMetrics(w http.ResponseWriter, r *http.Request) {
return
}
- sumAmount := db.DB.TotalPaymentsByDateRange(request)
+ sumAmount := db.DB.TotalPaymentsByDateRange(request, workspace)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(sumAmount)
@@ -114,6 +116,8 @@ func PeopleMetrics(w http.ResponseWriter, r *http.Request) {
func (mh *metricHandler) BountyMetrics(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
pubKeyFromAuth, _ := ctx.Value(auth.ContextKey).(string)
+ keys := r.URL.Query()
+ workspace := keys.Get("workspace")
if pubKeyFromAuth == "" {
fmt.Println("no pubkey from auth")
@@ -146,20 +150,22 @@ func (mh *metricHandler) BountyMetrics(w http.ResponseWriter, r *http.Request) {
}
}
- totalBountiesPosted := mh.db.TotalBountiesPosted(request)
- totalBountiesPaid := mh.db.TotalPaidBounties(request)
- bountiesPaidPercentage := mh.db.BountiesPaidPercentage(request)
- totalSatsPosted := mh.db.TotalSatsPosted(request)
- totalSatsPaid := mh.db.TotalSatsPaid(request)
- satsPaidPercentage := mh.db.SatsPaidPercentage(request)
- avgPaidDays := mh.db.AveragePaidTime(request)
- avgCompletedDays := mh.db.AverageCompletedTime(request)
- uniqueHuntersPaid := mh.db.TotalHuntersPaid(request)
- newHuntersPaid := mh.db.NewHuntersPaid(request)
+ totalBountiesPosted := mh.db.TotalBountiesPosted(request, workspace)
+ totalBountiesPaid := mh.db.TotalPaidBounties(request, workspace)
+ totalBountiesAssigned := mh.db.TotalAssignedBounties(request, workspace)
+ bountiesPaidPercentage := mh.db.BountiesPaidPercentage(request, workspace)
+ totalSatsPosted := mh.db.TotalSatsPosted(request, workspace)
+ totalSatsPaid := mh.db.TotalSatsPaid(request, workspace)
+ satsPaidPercentage := mh.db.SatsPaidPercentage(request, workspace)
+ avgPaidDays := mh.db.AveragePaidTime(request, workspace)
+ avgCompletedDays := mh.db.AverageCompletedTime(request, workspace)
+ uniqueHuntersPaid := mh.db.TotalHuntersPaid(request, workspace)
+ newHuntersPaid := mh.db.NewHuntersPaid(request, workspace)
bountyMetrics := db.BountyMetrics{
BountiesPosted: totalBountiesPosted,
BountiesPaid: totalBountiesPaid,
+ BountiesAssigned: totalBountiesAssigned,
BountiesPaidPercentage: bountiesPaidPercentage,
SatsPosted: totalSatsPosted,
SatsPaid: totalSatsPaid,
@@ -300,46 +306,65 @@ func MetricsCsv(w http.ResponseWriter, r *http.Request) {
}
}
-func (mh *metricHandler) GetMetricsBountiesData(metricBounties []db.Bounty) []db.BountyData {
+func GetAdminWorkspaces(w http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
+ pubKeyFromAuth, _ := ctx.Value(auth.ContextKey).(string)
+
+ if pubKeyFromAuth == "" {
+ fmt.Println("no pubkey from auth")
+ w.WriteHeader(http.StatusUnauthorized)
+ return
+ }
+
+ workspaces := db.DB.GetWorkspaces(r)
+ w.WriteHeader(http.StatusOK)
+ json.NewEncoder(w).Encode(workspaces)
+}
+
+func (mh *metricHandler) GetMetricsBountiesData(metricBounties []db.NewBounty) []db.BountyData {
var metricBountiesData []db.BountyData
for _, bounty := range metricBounties {
bountyOwner := mh.db.GetPersonByPubkey(bounty.OwnerID)
bountyAssignee := mh.db.GetPersonByPubkey(bounty.Assignee)
- organization := mh.db.GetWorkspaceByUuid(bounty.OrgUuid)
+ workspace := mh.db.GetWorkspaceByUuid(bounty.WorkspaceUuid)
bountyData := db.BountyData{
- Bounty: bounty,
- BountyId: bounty.ID,
- Person: bountyOwner,
- BountyCreated: bounty.Created,
- BountyDescription: bounty.Description,
- BountyUpdated: bounty.Updated,
- AssigneeId: bountyAssignee.ID,
- AssigneeImg: bountyAssignee.Img,
- AssigneeAlias: bountyAssignee.OwnerAlias,
- AssigneeDescription: bountyAssignee.Description,
- AssigneeRouteHint: bountyAssignee.OwnerRouteHint,
- BountyOwnerId: bountyOwner.ID,
- OwnerUuid: bountyOwner.Uuid,
- OwnerDescription: bountyOwner.Description,
- OwnerUniqueName: bountyOwner.UniqueName,
- OwnerImg: bountyOwner.Img,
- OrganizationName: organization.Name,
- OrganizationImg: organization.Img,
- WorkspaceUuid: organization.Uuid,
- WorkspaceDescription: organization.Description,
+ NewBounty: bounty,
+ BountyId: bounty.ID,
+ Person: bountyOwner,
+ BountyCreated: bounty.Created,
+ BountyDescription: bounty.Description,
+ BountyUpdated: bounty.Updated,
+ AssigneeId: bountyAssignee.ID,
+ AssigneeImg: bountyAssignee.Img,
+ AssigneeAlias: bountyAssignee.OwnerAlias,
+ AssigneeDescription: bountyAssignee.Description,
+ AssigneeRouteHint: bountyAssignee.OwnerRouteHint,
+ BountyOwnerId: bountyOwner.ID,
+ OwnerUuid: bountyOwner.Uuid,
+ OwnerDescription: bountyOwner.Description,
+ OwnerUniqueName: bountyOwner.UniqueName,
+ OwnerImg: bountyOwner.Img,
+ OrganizationName: workspace.Name,
+ OrganizationImg: workspace.Img,
+ OrganizationUuid: workspace.Uuid,
+ OrganizationDescription: workspace.Description,
+ WorkspaceName: workspace.Name,
+ WorkspaceImg: workspace.Img,
+ WorkspaceUuid: workspace.Uuid,
+ WorkspaceDescription: workspace.Description,
}
metricBountiesData = append(metricBountiesData, bountyData)
}
return metricBountiesData
}
-func getMetricsBountyCsv(metricBounties []db.Bounty) []db.MetricsBountyCsv {
+func getMetricsBountyCsv(metricBounties []db.NewBounty) []db.MetricsBountyCsv {
var metricBountiesCsv []db.MetricsBountyCsv
for _, bounty := range metricBounties {
bountyOwner := db.DB.GetPersonByPubkey(bounty.OwnerID)
bountyAssignee := db.DB.GetPersonByPubkey(bounty.Assignee)
- organization := db.DB.GetWorkspaceByUuid(bounty.OrgUuid)
+ workspace := db.DB.GetWorkspaceByUuid(bounty.WorkspaceUuid)
bountyLink := fmt.Sprintf("https://community.sphinx.chat/bounty/%d", bounty.ID)
bountyStatus := "Open"
@@ -353,7 +378,7 @@ func getMetricsBountyCsv(metricBounties []db.Bounty) []db.MetricsBountyCsv {
tm := time.Unix(bounty.Created, 0)
bountyCsv := db.MetricsBountyCsv{
DatePosted: &tm,
- Organization: organization.Name,
+ Organization: workspace.Name,
BountyAmount: bounty.Price,
Provider: bountyOwner.OwnerAlias,
Hunter: bountyAssignee.OwnerAlias,
diff --git a/handlers/metrics_test.go b/handlers/metrics_test.go
index 38695f8ac..d278831a4 100644
--- a/handlers/metrics_test.go
+++ b/handlers/metrics_test.go
@@ -58,30 +58,34 @@ func TestBountyMetrics(t *testing.T) {
db.RedisError = errors.New("redis not initialized")
rr := httptest.NewRecorder()
handler := http.HandlerFunc(mh.BountyMetrics)
+ workspace := "test-workspace"
+
dateRange := db.PaymentDateRange{
StartDate: "1111",
EndDate: "2222",
}
body, _ := json.Marshal(dateRange)
- req, err := http.NewRequestWithContext(ctx, http.MethodPost, "/bounty_stats", bytes.NewReader(body))
+ req, err := http.NewRequestWithContext(ctx, http.MethodPost, "/bounty_stats?workspace="+workspace, bytes.NewReader(body))
if err != nil {
t.Fatal(err)
}
- mockDb.On("TotalBountiesPosted", dateRange).Return(int64(1)).Once()
- mockDb.On("TotalPaidBounties", dateRange).Return(int64(1)).Once()
- mockDb.On("BountiesPaidPercentage", dateRange).Return(uint(1)).Once()
- mockDb.On("TotalSatsPosted", dateRange).Return(uint(1)).Once()
- mockDb.On("TotalSatsPaid", dateRange).Return(uint(1)).Once()
- mockDb.On("SatsPaidPercentage", dateRange).Return(uint(1)).Once()
- mockDb.On("AveragePaidTime", dateRange).Return(uint(1)).Once()
- mockDb.On("AverageCompletedTime", dateRange).Return(uint(1)).Once()
- mockDb.On("TotalHuntersPaid", dateRange).Return(int64(1)).Once()
- mockDb.On("NewHuntersPaid", dateRange).Return(int64(1)).Once()
+ mockDb.On("TotalBountiesPosted", dateRange, workspace).Return(int64(1)).Once()
+ mockDb.On("TotalPaidBounties", dateRange, workspace).Return(int64(1)).Once()
+ mockDb.On("TotalAssignedBounties", dateRange, workspace).Return(int64(2)).Once()
+ mockDb.On("BountiesPaidPercentage", dateRange, workspace).Return(uint(1)).Once()
+ mockDb.On("TotalSatsPosted", dateRange, workspace).Return(uint(1)).Once()
+ mockDb.On("TotalSatsPaid", dateRange, workspace).Return(uint(1)).Once()
+ mockDb.On("SatsPaidPercentage", dateRange, workspace).Return(uint(1)).Once()
+ mockDb.On("AveragePaidTime", dateRange, workspace).Return(uint(1)).Once()
+ mockDb.On("AverageCompletedTime", dateRange, workspace).Return(uint(1)).Once()
+ mockDb.On("TotalHuntersPaid", dateRange, workspace).Return(int64(1)).Once()
+ mockDb.On("NewHuntersPaid", dateRange, workspace).Return(int64(1)).Once()
handler.ServeHTTP(rr, req)
expectedMetricRes := db.BountyMetrics{
BountiesPosted: 1,
BountiesPaid: 1,
+ BountiesAssigned: 2,
BountiesPaidPercentage: 1,
SatsPosted: 1,
SatsPaid: 1,
@@ -148,7 +152,7 @@ func TestMetricsBounties(t *testing.T) {
t.Fatal(err)
}
- bounties := []db.Bounty{
+ bounties := []db.NewBounty{
{
ID: 1,
OwnerID: "owner-1",
@@ -194,7 +198,7 @@ func TestMetricsBounties(t *testing.T) {
req.URL.RawQuery = "provider=provider1,provider2"
// Mock bounties data for multiple providers
- bounties := []db.Bounty{
+ bounties := []db.NewBounty{
{
ID: 1,
OwnerID: "provider1",
diff --git a/mocks/Database.go b/mocks/Database.go
index aeff38189..06093fbe1 100644
--- a/mocks/Database.go
+++ b/mocks/Database.go
@@ -311,17 +311,17 @@ func (_c *Database_AddUserInvoiceData_Call) RunAndReturn(run func(db.UserInvoice
return _c
}
-// AverageCompletedTime provides a mock function with given fields: r
-func (_m *Database) AverageCompletedTime(r db.PaymentDateRange) uint {
- ret := _m.Called(r)
+// AverageCompletedTime provides a mock function with given fields: r, workspace
+func (_m *Database) AverageCompletedTime(r db.PaymentDateRange, workspace string) uint {
+ ret := _m.Called(r, workspace)
if len(ret) == 0 {
panic("no return value specified for AverageCompletedTime")
}
var r0 uint
- if rf, ok := ret.Get(0).(func(db.PaymentDateRange) uint); ok {
- r0 = rf(r)
+ if rf, ok := ret.Get(0).(func(db.PaymentDateRange, string) uint); ok {
+ r0 = rf(r, workspace)
} else {
r0 = ret.Get(0).(uint)
}
@@ -336,13 +336,14 @@ type Database_AverageCompletedTime_Call struct {
// AverageCompletedTime is a helper method to define mock.On call
// - r db.PaymentDateRange
-func (_e *Database_Expecter) AverageCompletedTime(r interface{}) *Database_AverageCompletedTime_Call {
- return &Database_AverageCompletedTime_Call{Call: _e.mock.On("AverageCompletedTime", r)}
+// - workspace string
+func (_e *Database_Expecter) AverageCompletedTime(r interface{}, workspace interface{}) *Database_AverageCompletedTime_Call {
+ return &Database_AverageCompletedTime_Call{Call: _e.mock.On("AverageCompletedTime", r, workspace)}
}
-func (_c *Database_AverageCompletedTime_Call) Run(run func(r db.PaymentDateRange)) *Database_AverageCompletedTime_Call {
+func (_c *Database_AverageCompletedTime_Call) Run(run func(r db.PaymentDateRange, workspace string)) *Database_AverageCompletedTime_Call {
_c.Call.Run(func(args mock.Arguments) {
- run(args[0].(db.PaymentDateRange))
+ run(args[0].(db.PaymentDateRange), args[1].(string))
})
return _c
}
@@ -352,22 +353,22 @@ func (_c *Database_AverageCompletedTime_Call) Return(_a0 uint) *Database_Average
return _c
}
-func (_c *Database_AverageCompletedTime_Call) RunAndReturn(run func(db.PaymentDateRange) uint) *Database_AverageCompletedTime_Call {
+func (_c *Database_AverageCompletedTime_Call) RunAndReturn(run func(db.PaymentDateRange, string) uint) *Database_AverageCompletedTime_Call {
_c.Call.Return(run)
return _c
}
-// AveragePaidTime provides a mock function with given fields: r
-func (_m *Database) AveragePaidTime(r db.PaymentDateRange) uint {
- ret := _m.Called(r)
+// AveragePaidTime provides a mock function with given fields: r, workspace
+func (_m *Database) AveragePaidTime(r db.PaymentDateRange, workspace string) uint {
+ ret := _m.Called(r, workspace)
if len(ret) == 0 {
panic("no return value specified for AveragePaidTime")
}
var r0 uint
- if rf, ok := ret.Get(0).(func(db.PaymentDateRange) uint); ok {
- r0 = rf(r)
+ if rf, ok := ret.Get(0).(func(db.PaymentDateRange, string) uint); ok {
+ r0 = rf(r, workspace)
} else {
r0 = ret.Get(0).(uint)
}
@@ -382,13 +383,14 @@ type Database_AveragePaidTime_Call struct {
// AveragePaidTime is a helper method to define mock.On call
// - r db.PaymentDateRange
-func (_e *Database_Expecter) AveragePaidTime(r interface{}) *Database_AveragePaidTime_Call {
- return &Database_AveragePaidTime_Call{Call: _e.mock.On("AveragePaidTime", r)}
+// - workspace string
+func (_e *Database_Expecter) AveragePaidTime(r interface{}, workspace interface{}) *Database_AveragePaidTime_Call {
+ return &Database_AveragePaidTime_Call{Call: _e.mock.On("AveragePaidTime", r, workspace)}
}
-func (_c *Database_AveragePaidTime_Call) Run(run func(r db.PaymentDateRange)) *Database_AveragePaidTime_Call {
+func (_c *Database_AveragePaidTime_Call) Run(run func(r db.PaymentDateRange, workspace string)) *Database_AveragePaidTime_Call {
_c.Call.Run(func(args mock.Arguments) {
- run(args[0].(db.PaymentDateRange))
+ run(args[0].(db.PaymentDateRange), args[1].(string))
})
return _c
}
@@ -398,22 +400,22 @@ func (_c *Database_AveragePaidTime_Call) Return(_a0 uint) *Database_AveragePaidT
return _c
}
-func (_c *Database_AveragePaidTime_Call) RunAndReturn(run func(db.PaymentDateRange) uint) *Database_AveragePaidTime_Call {
+func (_c *Database_AveragePaidTime_Call) RunAndReturn(run func(db.PaymentDateRange, string) uint) *Database_AveragePaidTime_Call {
_c.Call.Return(run)
return _c
}
-// BountiesPaidPercentage provides a mock function with given fields: r
-func (_m *Database) BountiesPaidPercentage(r db.PaymentDateRange) uint {
- ret := _m.Called(r)
+// BountiesPaidPercentage provides a mock function with given fields: r, workspace
+func (_m *Database) BountiesPaidPercentage(r db.PaymentDateRange, workspace string) uint {
+ ret := _m.Called(r, workspace)
if len(ret) == 0 {
panic("no return value specified for BountiesPaidPercentage")
}
var r0 uint
- if rf, ok := ret.Get(0).(func(db.PaymentDateRange) uint); ok {
- r0 = rf(r)
+ if rf, ok := ret.Get(0).(func(db.PaymentDateRange, string) uint); ok {
+ r0 = rf(r, workspace)
} else {
r0 = ret.Get(0).(uint)
}
@@ -428,13 +430,14 @@ type Database_BountiesPaidPercentage_Call struct {
// BountiesPaidPercentage is a helper method to define mock.On call
// - r db.PaymentDateRange
-func (_e *Database_Expecter) BountiesPaidPercentage(r interface{}) *Database_BountiesPaidPercentage_Call {
- return &Database_BountiesPaidPercentage_Call{Call: _e.mock.On("BountiesPaidPercentage", r)}
+// - workspace string
+func (_e *Database_Expecter) BountiesPaidPercentage(r interface{}, workspace interface{}) *Database_BountiesPaidPercentage_Call {
+ return &Database_BountiesPaidPercentage_Call{Call: _e.mock.On("BountiesPaidPercentage", r, workspace)}
}
-func (_c *Database_BountiesPaidPercentage_Call) Run(run func(r db.PaymentDateRange)) *Database_BountiesPaidPercentage_Call {
+func (_c *Database_BountiesPaidPercentage_Call) Run(run func(r db.PaymentDateRange, workspace string)) *Database_BountiesPaidPercentage_Call {
_c.Call.Run(func(args mock.Arguments) {
- run(args[0].(db.PaymentDateRange))
+ run(args[0].(db.PaymentDateRange), args[1].(string))
})
return _c
}
@@ -444,7 +447,7 @@ func (_c *Database_BountiesPaidPercentage_Call) Return(_a0 uint) *Database_Bount
return _c
}
-func (_c *Database_BountiesPaidPercentage_Call) RunAndReturn(run func(db.PaymentDateRange) uint) *Database_BountiesPaidPercentage_Call {
+func (_c *Database_BountiesPaidPercentage_Call) RunAndReturn(run func(db.PaymentDateRange, string) uint) *Database_BountiesPaidPercentage_Call {
_c.Call.Return(run)
return _c
}
@@ -927,6 +930,62 @@ func (_c *Database_CreateOrEditBounty_Call) RunAndReturn(run func(db.NewBounty)
return _c
}
+// CreateOrEditFeature provides a mock function with given fields: m
+func (_m *Database) CreateOrEditFeature(m db.WorkspaceFeatures) (db.WorkspaceFeatures, error) {
+ ret := _m.Called(m)
+
+ if len(ret) == 0 {
+ panic("no return value specified for CreateOrEditFeature")
+ }
+
+ var r0 db.WorkspaceFeatures
+ var r1 error
+ if rf, ok := ret.Get(0).(func(db.WorkspaceFeatures) (db.WorkspaceFeatures, error)); ok {
+ return rf(m)
+ }
+ if rf, ok := ret.Get(0).(func(db.WorkspaceFeatures) db.WorkspaceFeatures); ok {
+ r0 = rf(m)
+ } else {
+ r0 = ret.Get(0).(db.WorkspaceFeatures)
+ }
+
+ if rf, ok := ret.Get(1).(func(db.WorkspaceFeatures) error); ok {
+ r1 = rf(m)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// Database_CreateOrEditFeature_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateOrEditFeature'
+type Database_CreateOrEditFeature_Call struct {
+ *mock.Call
+}
+
+// CreateOrEditFeature is a helper method to define mock.On call
+// - m db.WorkspaceFeatures
+func (_e *Database_Expecter) CreateOrEditFeature(m interface{}) *Database_CreateOrEditFeature_Call {
+ return &Database_CreateOrEditFeature_Call{Call: _e.mock.On("CreateOrEditFeature", m)}
+}
+
+func (_c *Database_CreateOrEditFeature_Call) Run(run func(m db.WorkspaceFeatures)) *Database_CreateOrEditFeature_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(db.WorkspaceFeatures))
+ })
+ return _c
+}
+
+func (_c *Database_CreateOrEditFeature_Call) Return(_a0 db.WorkspaceFeatures, _a1 error) *Database_CreateOrEditFeature_Call {
+ _c.Call.Return(_a0, _a1)
+ return _c
+}
+
+func (_c *Database_CreateOrEditFeature_Call) RunAndReturn(run func(db.WorkspaceFeatures) (db.WorkspaceFeatures, error)) *Database_CreateOrEditFeature_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
// CreateOrEditPerson provides a mock function with given fields: m
func (_m *Database) CreateOrEditPerson(m db.Person) (db.Person, error) {
ret := _m.Called(m)
@@ -1831,19 +1890,19 @@ func (_c *Database_GetBotsByOwner_Call) RunAndReturn(run func(string) []db.Bot)
}
// GetBountiesByDateRange provides a mock function with given fields: r, re
-func (_m *Database) GetBountiesByDateRange(r db.PaymentDateRange, re *http.Request) []db.Bounty {
+func (_m *Database) GetBountiesByDateRange(r db.PaymentDateRange, re *http.Request) []db.NewBounty {
ret := _m.Called(r, re)
if len(ret) == 0 {
panic("no return value specified for GetBountiesByDateRange")
}
- var r0 []db.Bounty
- if rf, ok := ret.Get(0).(func(db.PaymentDateRange, *http.Request) []db.Bounty); ok {
+ var r0 []db.NewBounty
+ if rf, ok := ret.Get(0).(func(db.PaymentDateRange, *http.Request) []db.NewBounty); ok {
r0 = rf(r, re)
} else {
if ret.Get(0) != nil {
- r0 = ret.Get(0).([]db.Bounty)
+ r0 = ret.Get(0).([]db.NewBounty)
}
}
@@ -1869,12 +1928,12 @@ func (_c *Database_GetBountiesByDateRange_Call) Run(run func(r db.PaymentDateRan
return _c
}
-func (_c *Database_GetBountiesByDateRange_Call) Return(_a0 []db.Bounty) *Database_GetBountiesByDateRange_Call {
+func (_c *Database_GetBountiesByDateRange_Call) Return(_a0 []db.NewBounty) *Database_GetBountiesByDateRange_Call {
_c.Call.Return(_a0)
return _c
}
-func (_c *Database_GetBountiesByDateRange_Call) RunAndReturn(run func(db.PaymentDateRange, *http.Request) []db.Bounty) *Database_GetBountiesByDateRange_Call {
+func (_c *Database_GetBountiesByDateRange_Call) RunAndReturn(run func(db.PaymentDateRange, *http.Request) []db.NewBounty) *Database_GetBountiesByDateRange_Call {
_c.Call.Return(run)
return _c
}
@@ -2576,6 +2635,100 @@ func (_c *Database_GetCreatedBounties_Call) RunAndReturn(run func(*http.Request)
return _c
}
+// GetFeatureByUuid provides a mock function with given fields: uuid
+func (_m *Database) GetFeatureByUuid(uuid string) db.WorkspaceFeatures {
+ ret := _m.Called(uuid)
+
+ if len(ret) == 0 {
+ panic("no return value specified for GetFeatureByUuid")
+ }
+
+ var r0 db.WorkspaceFeatures
+ if rf, ok := ret.Get(0).(func(string) db.WorkspaceFeatures); ok {
+ r0 = rf(uuid)
+ } else {
+ r0 = ret.Get(0).(db.WorkspaceFeatures)
+ }
+
+ return r0
+}
+
+// Database_GetFeatureByUuid_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetFeatureByUuid'
+type Database_GetFeatureByUuid_Call struct {
+ *mock.Call
+}
+
+// GetFeatureByUuid is a helper method to define mock.On call
+// - uuid string
+func (_e *Database_Expecter) GetFeatureByUuid(uuid interface{}) *Database_GetFeatureByUuid_Call {
+ return &Database_GetFeatureByUuid_Call{Call: _e.mock.On("GetFeatureByUuid", uuid)}
+}
+
+func (_c *Database_GetFeatureByUuid_Call) Run(run func(uuid string)) *Database_GetFeatureByUuid_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(string))
+ })
+ return _c
+}
+
+func (_c *Database_GetFeatureByUuid_Call) Return(_a0 db.WorkspaceFeatures) *Database_GetFeatureByUuid_Call {
+ _c.Call.Return(_a0)
+ return _c
+}
+
+func (_c *Database_GetFeatureByUuid_Call) RunAndReturn(run func(string) db.WorkspaceFeatures) *Database_GetFeatureByUuid_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// GetFeaturesByWorkspaceUuid provides a mock function with given fields: uuid
+func (_m *Database) GetFeaturesByWorkspaceUuid(uuid string) []db.WorkspaceFeatures {
+ ret := _m.Called(uuid)
+
+ if len(ret) == 0 {
+ panic("no return value specified for GetFeaturesByWorkspaceUuid")
+ }
+
+ var r0 []db.WorkspaceFeatures
+ if rf, ok := ret.Get(0).(func(string) []db.WorkspaceFeatures); ok {
+ r0 = rf(uuid)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).([]db.WorkspaceFeatures)
+ }
+ }
+
+ return r0
+}
+
+// Database_GetFeaturesByWorkspaceUuid_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetFeaturesByWorkspaceUuid'
+type Database_GetFeaturesByWorkspaceUuid_Call struct {
+ *mock.Call
+}
+
+// GetFeaturesByWorkspaceUuid is a helper method to define mock.On call
+// - uuid string
+func (_e *Database_Expecter) GetFeaturesByWorkspaceUuid(uuid interface{}) *Database_GetFeaturesByWorkspaceUuid_Call {
+ return &Database_GetFeaturesByWorkspaceUuid_Call{Call: _e.mock.On("GetFeaturesByWorkspaceUuid", uuid)}
+}
+
+func (_c *Database_GetFeaturesByWorkspaceUuid_Call) Run(run func(uuid string)) *Database_GetFeaturesByWorkspaceUuid_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(string))
+ })
+ return _c
+}
+
+func (_c *Database_GetFeaturesByWorkspaceUuid_Call) Return(_a0 []db.WorkspaceFeatures) *Database_GetFeaturesByWorkspaceUuid_Call {
+ _c.Call.Return(_a0)
+ return _c
+}
+
+func (_c *Database_GetFeaturesByWorkspaceUuid_Call) RunAndReturn(run func(string) []db.WorkspaceFeatures) *Database_GetFeaturesByWorkspaceUuid_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
// GetFilterStatusCount provides a mock function with given fields:
func (_m *Database) GetFilterStatusCount() db.FilterStattuCount {
ret := _m.Called()
@@ -5139,17 +5292,17 @@ func (_c *Database_GetWorkspacesCount_Call) RunAndReturn(run func() int64) *Data
return _c
}
-// NewHuntersPaid provides a mock function with given fields: r
-func (_m *Database) NewHuntersPaid(r db.PaymentDateRange) int64 {
- ret := _m.Called(r)
+// NewHuntersPaid provides a mock function with given fields: r, workspace
+func (_m *Database) NewHuntersPaid(r db.PaymentDateRange, workspace string) int64 {
+ ret := _m.Called(r, workspace)
if len(ret) == 0 {
panic("no return value specified for NewHuntersPaid")
}
var r0 int64
- if rf, ok := ret.Get(0).(func(db.PaymentDateRange) int64); ok {
- r0 = rf(r)
+ if rf, ok := ret.Get(0).(func(db.PaymentDateRange, string) int64); ok {
+ r0 = rf(r, workspace)
} else {
r0 = ret.Get(0).(int64)
}
@@ -5164,13 +5317,14 @@ type Database_NewHuntersPaid_Call struct {
// NewHuntersPaid is a helper method to define mock.On call
// - r db.PaymentDateRange
-func (_e *Database_Expecter) NewHuntersPaid(r interface{}) *Database_NewHuntersPaid_Call {
- return &Database_NewHuntersPaid_Call{Call: _e.mock.On("NewHuntersPaid", r)}
+// - workspace string
+func (_e *Database_Expecter) NewHuntersPaid(r interface{}, workspace interface{}) *Database_NewHuntersPaid_Call {
+ return &Database_NewHuntersPaid_Call{Call: _e.mock.On("NewHuntersPaid", r, workspace)}
}
-func (_c *Database_NewHuntersPaid_Call) Run(run func(r db.PaymentDateRange)) *Database_NewHuntersPaid_Call {
+func (_c *Database_NewHuntersPaid_Call) Run(run func(r db.PaymentDateRange, workspace string)) *Database_NewHuntersPaid_Call {
_c.Call.Run(func(args mock.Arguments) {
- run(args[0].(db.PaymentDateRange))
+ run(args[0].(db.PaymentDateRange), args[1].(string))
})
return _c
}
@@ -5180,7 +5334,7 @@ func (_c *Database_NewHuntersPaid_Call) Return(_a0 int64) *Database_NewHuntersPa
return _c
}
-func (_c *Database_NewHuntersPaid_Call) RunAndReturn(run func(db.PaymentDateRange) int64) *Database_NewHuntersPaid_Call {
+func (_c *Database_NewHuntersPaid_Call) RunAndReturn(run func(db.PaymentDateRange, string) int64) *Database_NewHuntersPaid_Call {
_c.Call.Return(run)
return _c
}
@@ -5274,17 +5428,17 @@ func (_c *Database_ProcessAlerts_Call) RunAndReturn(run func(db.Person)) *Databa
return _c
}
-// SatsPaidPercentage provides a mock function with given fields: r
-func (_m *Database) SatsPaidPercentage(r db.PaymentDateRange) uint {
- ret := _m.Called(r)
+// SatsPaidPercentage provides a mock function with given fields: r, workspace
+func (_m *Database) SatsPaidPercentage(r db.PaymentDateRange, workspace string) uint {
+ ret := _m.Called(r, workspace)
if len(ret) == 0 {
panic("no return value specified for SatsPaidPercentage")
}
var r0 uint
- if rf, ok := ret.Get(0).(func(db.PaymentDateRange) uint); ok {
- r0 = rf(r)
+ if rf, ok := ret.Get(0).(func(db.PaymentDateRange, string) uint); ok {
+ r0 = rf(r, workspace)
} else {
r0 = ret.Get(0).(uint)
}
@@ -5299,13 +5453,14 @@ type Database_SatsPaidPercentage_Call struct {
// SatsPaidPercentage is a helper method to define mock.On call
// - r db.PaymentDateRange
-func (_e *Database_Expecter) SatsPaidPercentage(r interface{}) *Database_SatsPaidPercentage_Call {
- return &Database_SatsPaidPercentage_Call{Call: _e.mock.On("SatsPaidPercentage", r)}
+// - workspace string
+func (_e *Database_Expecter) SatsPaidPercentage(r interface{}, workspace interface{}) *Database_SatsPaidPercentage_Call {
+ return &Database_SatsPaidPercentage_Call{Call: _e.mock.On("SatsPaidPercentage", r, workspace)}
}
-func (_c *Database_SatsPaidPercentage_Call) Run(run func(r db.PaymentDateRange)) *Database_SatsPaidPercentage_Call {
+func (_c *Database_SatsPaidPercentage_Call) Run(run func(r db.PaymentDateRange, workspace string)) *Database_SatsPaidPercentage_Call {
_c.Call.Run(func(args mock.Arguments) {
- run(args[0].(db.PaymentDateRange))
+ run(args[0].(db.PaymentDateRange), args[1].(string))
})
return _c
}
@@ -5315,7 +5470,7 @@ func (_c *Database_SatsPaidPercentage_Call) Return(_a0 uint) *Database_SatsPaidP
return _c
}
-func (_c *Database_SatsPaidPercentage_Call) RunAndReturn(run func(db.PaymentDateRange) uint) *Database_SatsPaidPercentage_Call {
+func (_c *Database_SatsPaidPercentage_Call) RunAndReturn(run func(db.PaymentDateRange, string) uint) *Database_SatsPaidPercentage_Call {
_c.Call.Return(run)
return _c
}
@@ -5468,17 +5623,64 @@ func (_c *Database_SearchTribes_Call) RunAndReturn(run func(string) []db.Tribe)
return _c
}
-// TotalBountiesPosted provides a mock function with given fields: r
-func (_m *Database) TotalBountiesPosted(r db.PaymentDateRange) int64 {
- ret := _m.Called(r)
+// TotalAssignedBounties provides a mock function with given fields: r, workspace
+func (_m *Database) TotalAssignedBounties(r db.PaymentDateRange, workspace string) int64 {
+ ret := _m.Called(r, workspace)
+
+ if len(ret) == 0 {
+ panic("no return value specified for TotalAssignedBounties")
+ }
+
+ var r0 int64
+ if rf, ok := ret.Get(0).(func(db.PaymentDateRange, string) int64); ok {
+ r0 = rf(r, workspace)
+ } else {
+ r0 = ret.Get(0).(int64)
+ }
+
+ return r0
+}
+
+// Database_TotalAssignedBounties_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TotalAssignedBounties'
+type Database_TotalAssignedBounties_Call struct {
+ *mock.Call
+}
+
+// TotalAssignedBounties is a helper method to define mock.On call
+// - r db.PaymentDateRange
+// - workspace string
+func (_e *Database_Expecter) TotalAssignedBounties(r interface{}, workspace interface{}) *Database_TotalAssignedBounties_Call {
+ return &Database_TotalAssignedBounties_Call{Call: _e.mock.On("TotalAssignedBounties", r, workspace)}
+}
+
+func (_c *Database_TotalAssignedBounties_Call) Run(run func(r db.PaymentDateRange, workspace string)) *Database_TotalAssignedBounties_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(db.PaymentDateRange), args[1].(string))
+ })
+ return _c
+}
+
+func (_c *Database_TotalAssignedBounties_Call) Return(_a0 int64) *Database_TotalAssignedBounties_Call {
+ _c.Call.Return(_a0)
+ return _c
+}
+
+func (_c *Database_TotalAssignedBounties_Call) RunAndReturn(run func(db.PaymentDateRange, string) int64) *Database_TotalAssignedBounties_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// TotalBountiesPosted provides a mock function with given fields: r, workspace
+func (_m *Database) TotalBountiesPosted(r db.PaymentDateRange, workspace string) int64 {
+ ret := _m.Called(r, workspace)
if len(ret) == 0 {
panic("no return value specified for TotalBountiesPosted")
}
var r0 int64
- if rf, ok := ret.Get(0).(func(db.PaymentDateRange) int64); ok {
- r0 = rf(r)
+ if rf, ok := ret.Get(0).(func(db.PaymentDateRange, string) int64); ok {
+ r0 = rf(r, workspace)
} else {
r0 = ret.Get(0).(int64)
}
@@ -5493,13 +5695,14 @@ type Database_TotalBountiesPosted_Call struct {
// TotalBountiesPosted is a helper method to define mock.On call
// - r db.PaymentDateRange
-func (_e *Database_Expecter) TotalBountiesPosted(r interface{}) *Database_TotalBountiesPosted_Call {
- return &Database_TotalBountiesPosted_Call{Call: _e.mock.On("TotalBountiesPosted", r)}
+// - workspace string
+func (_e *Database_Expecter) TotalBountiesPosted(r interface{}, workspace interface{}) *Database_TotalBountiesPosted_Call {
+ return &Database_TotalBountiesPosted_Call{Call: _e.mock.On("TotalBountiesPosted", r, workspace)}
}
-func (_c *Database_TotalBountiesPosted_Call) Run(run func(r db.PaymentDateRange)) *Database_TotalBountiesPosted_Call {
+func (_c *Database_TotalBountiesPosted_Call) Run(run func(r db.PaymentDateRange, workspace string)) *Database_TotalBountiesPosted_Call {
_c.Call.Run(func(args mock.Arguments) {
- run(args[0].(db.PaymentDateRange))
+ run(args[0].(db.PaymentDateRange), args[1].(string))
})
return _c
}
@@ -5509,22 +5712,22 @@ func (_c *Database_TotalBountiesPosted_Call) Return(_a0 int64) *Database_TotalBo
return _c
}
-func (_c *Database_TotalBountiesPosted_Call) RunAndReturn(run func(db.PaymentDateRange) int64) *Database_TotalBountiesPosted_Call {
+func (_c *Database_TotalBountiesPosted_Call) RunAndReturn(run func(db.PaymentDateRange, string) int64) *Database_TotalBountiesPosted_Call {
_c.Call.Return(run)
return _c
}
-// TotalHuntersPaid provides a mock function with given fields: r
-func (_m *Database) TotalHuntersPaid(r db.PaymentDateRange) int64 {
- ret := _m.Called(r)
+// TotalHuntersPaid provides a mock function with given fields: r, workspace
+func (_m *Database) TotalHuntersPaid(r db.PaymentDateRange, workspace string) int64 {
+ ret := _m.Called(r, workspace)
if len(ret) == 0 {
panic("no return value specified for TotalHuntersPaid")
}
var r0 int64
- if rf, ok := ret.Get(0).(func(db.PaymentDateRange) int64); ok {
- r0 = rf(r)
+ if rf, ok := ret.Get(0).(func(db.PaymentDateRange, string) int64); ok {
+ r0 = rf(r, workspace)
} else {
r0 = ret.Get(0).(int64)
}
@@ -5539,13 +5742,14 @@ type Database_TotalHuntersPaid_Call struct {
// TotalHuntersPaid is a helper method to define mock.On call
// - r db.PaymentDateRange
-func (_e *Database_Expecter) TotalHuntersPaid(r interface{}) *Database_TotalHuntersPaid_Call {
- return &Database_TotalHuntersPaid_Call{Call: _e.mock.On("TotalHuntersPaid", r)}
+// - workspace string
+func (_e *Database_Expecter) TotalHuntersPaid(r interface{}, workspace interface{}) *Database_TotalHuntersPaid_Call {
+ return &Database_TotalHuntersPaid_Call{Call: _e.mock.On("TotalHuntersPaid", r, workspace)}
}
-func (_c *Database_TotalHuntersPaid_Call) Run(run func(r db.PaymentDateRange)) *Database_TotalHuntersPaid_Call {
+func (_c *Database_TotalHuntersPaid_Call) Run(run func(r db.PaymentDateRange, workspace string)) *Database_TotalHuntersPaid_Call {
_c.Call.Run(func(args mock.Arguments) {
- run(args[0].(db.PaymentDateRange))
+ run(args[0].(db.PaymentDateRange), args[1].(string))
})
return _c
}
@@ -5555,22 +5759,22 @@ func (_c *Database_TotalHuntersPaid_Call) Return(_a0 int64) *Database_TotalHunte
return _c
}
-func (_c *Database_TotalHuntersPaid_Call) RunAndReturn(run func(db.PaymentDateRange) int64) *Database_TotalHuntersPaid_Call {
+func (_c *Database_TotalHuntersPaid_Call) RunAndReturn(run func(db.PaymentDateRange, string) int64) *Database_TotalHuntersPaid_Call {
_c.Call.Return(run)
return _c
}
-// TotalPaidBounties provides a mock function with given fields: r
-func (_m *Database) TotalPaidBounties(r db.PaymentDateRange) int64 {
- ret := _m.Called(r)
+// TotalPaidBounties provides a mock function with given fields: r, workspace
+func (_m *Database) TotalPaidBounties(r db.PaymentDateRange, workspace string) int64 {
+ ret := _m.Called(r, workspace)
if len(ret) == 0 {
panic("no return value specified for TotalPaidBounties")
}
var r0 int64
- if rf, ok := ret.Get(0).(func(db.PaymentDateRange) int64); ok {
- r0 = rf(r)
+ if rf, ok := ret.Get(0).(func(db.PaymentDateRange, string) int64); ok {
+ r0 = rf(r, workspace)
} else {
r0 = ret.Get(0).(int64)
}
@@ -5585,13 +5789,14 @@ type Database_TotalPaidBounties_Call struct {
// TotalPaidBounties is a helper method to define mock.On call
// - r db.PaymentDateRange
-func (_e *Database_Expecter) TotalPaidBounties(r interface{}) *Database_TotalPaidBounties_Call {
- return &Database_TotalPaidBounties_Call{Call: _e.mock.On("TotalPaidBounties", r)}
+// - workspace string
+func (_e *Database_Expecter) TotalPaidBounties(r interface{}, workspace interface{}) *Database_TotalPaidBounties_Call {
+ return &Database_TotalPaidBounties_Call{Call: _e.mock.On("TotalPaidBounties", r, workspace)}
}
-func (_c *Database_TotalPaidBounties_Call) Run(run func(r db.PaymentDateRange)) *Database_TotalPaidBounties_Call {
+func (_c *Database_TotalPaidBounties_Call) Run(run func(r db.PaymentDateRange, workspace string)) *Database_TotalPaidBounties_Call {
_c.Call.Run(func(args mock.Arguments) {
- run(args[0].(db.PaymentDateRange))
+ run(args[0].(db.PaymentDateRange), args[1].(string))
})
return _c
}
@@ -5601,22 +5806,22 @@ func (_c *Database_TotalPaidBounties_Call) Return(_a0 int64) *Database_TotalPaid
return _c
}
-func (_c *Database_TotalPaidBounties_Call) RunAndReturn(run func(db.PaymentDateRange) int64) *Database_TotalPaidBounties_Call {
+func (_c *Database_TotalPaidBounties_Call) RunAndReturn(run func(db.PaymentDateRange, string) int64) *Database_TotalPaidBounties_Call {
_c.Call.Return(run)
return _c
}
-// TotalSatsPaid provides a mock function with given fields: r
-func (_m *Database) TotalSatsPaid(r db.PaymentDateRange) uint {
- ret := _m.Called(r)
+// TotalSatsPaid provides a mock function with given fields: r, workspace
+func (_m *Database) TotalSatsPaid(r db.PaymentDateRange, workspace string) uint {
+ ret := _m.Called(r, workspace)
if len(ret) == 0 {
panic("no return value specified for TotalSatsPaid")
}
var r0 uint
- if rf, ok := ret.Get(0).(func(db.PaymentDateRange) uint); ok {
- r0 = rf(r)
+ if rf, ok := ret.Get(0).(func(db.PaymentDateRange, string) uint); ok {
+ r0 = rf(r, workspace)
} else {
r0 = ret.Get(0).(uint)
}
@@ -5631,13 +5836,14 @@ type Database_TotalSatsPaid_Call struct {
// TotalSatsPaid is a helper method to define mock.On call
// - r db.PaymentDateRange
-func (_e *Database_Expecter) TotalSatsPaid(r interface{}) *Database_TotalSatsPaid_Call {
- return &Database_TotalSatsPaid_Call{Call: _e.mock.On("TotalSatsPaid", r)}
+// - workspace string
+func (_e *Database_Expecter) TotalSatsPaid(r interface{}, workspace interface{}) *Database_TotalSatsPaid_Call {
+ return &Database_TotalSatsPaid_Call{Call: _e.mock.On("TotalSatsPaid", r, workspace)}
}
-func (_c *Database_TotalSatsPaid_Call) Run(run func(r db.PaymentDateRange)) *Database_TotalSatsPaid_Call {
+func (_c *Database_TotalSatsPaid_Call) Run(run func(r db.PaymentDateRange, workspace string)) *Database_TotalSatsPaid_Call {
_c.Call.Run(func(args mock.Arguments) {
- run(args[0].(db.PaymentDateRange))
+ run(args[0].(db.PaymentDateRange), args[1].(string))
})
return _c
}
@@ -5647,22 +5853,22 @@ func (_c *Database_TotalSatsPaid_Call) Return(_a0 uint) *Database_TotalSatsPaid_
return _c
}
-func (_c *Database_TotalSatsPaid_Call) RunAndReturn(run func(db.PaymentDateRange) uint) *Database_TotalSatsPaid_Call {
+func (_c *Database_TotalSatsPaid_Call) RunAndReturn(run func(db.PaymentDateRange, string) uint) *Database_TotalSatsPaid_Call {
_c.Call.Return(run)
return _c
}
-// TotalSatsPosted provides a mock function with given fields: r
-func (_m *Database) TotalSatsPosted(r db.PaymentDateRange) uint {
- ret := _m.Called(r)
+// TotalSatsPosted provides a mock function with given fields: r, workspace
+func (_m *Database) TotalSatsPosted(r db.PaymentDateRange, workspace string) uint {
+ ret := _m.Called(r, workspace)
if len(ret) == 0 {
panic("no return value specified for TotalSatsPosted")
}
var r0 uint
- if rf, ok := ret.Get(0).(func(db.PaymentDateRange) uint); ok {
- r0 = rf(r)
+ if rf, ok := ret.Get(0).(func(db.PaymentDateRange, string) uint); ok {
+ r0 = rf(r, workspace)
} else {
r0 = ret.Get(0).(uint)
}
@@ -5677,13 +5883,14 @@ type Database_TotalSatsPosted_Call struct {
// TotalSatsPosted is a helper method to define mock.On call
// - r db.PaymentDateRange
-func (_e *Database_Expecter) TotalSatsPosted(r interface{}) *Database_TotalSatsPosted_Call {
- return &Database_TotalSatsPosted_Call{Call: _e.mock.On("TotalSatsPosted", r)}
+// - workspace string
+func (_e *Database_Expecter) TotalSatsPosted(r interface{}, workspace interface{}) *Database_TotalSatsPosted_Call {
+ return &Database_TotalSatsPosted_Call{Call: _e.mock.On("TotalSatsPosted", r, workspace)}
}
-func (_c *Database_TotalSatsPosted_Call) Run(run func(r db.PaymentDateRange)) *Database_TotalSatsPosted_Call {
+func (_c *Database_TotalSatsPosted_Call) Run(run func(r db.PaymentDateRange, workspace string)) *Database_TotalSatsPosted_Call {
_c.Call.Run(func(args mock.Arguments) {
- run(args[0].(db.PaymentDateRange))
+ run(args[0].(db.PaymentDateRange), args[1].(string))
})
return _c
}
@@ -5693,7 +5900,7 @@ func (_c *Database_TotalSatsPosted_Call) Return(_a0 uint) *Database_TotalSatsPos
return _c
}
-func (_c *Database_TotalSatsPosted_Call) RunAndReturn(run func(db.PaymentDateRange) uint) *Database_TotalSatsPosted_Call {
+func (_c *Database_TotalSatsPosted_Call) RunAndReturn(run func(db.PaymentDateRange, string) uint) *Database_TotalSatsPosted_Call {
_c.Call.Return(run)
return _c
}
diff --git a/routes/features.go b/routes/features.go
new file mode 100644
index 000000000..0b2449399
--- /dev/null
+++ b/routes/features.go
@@ -0,0 +1,21 @@
+package routes
+
+import (
+ "github.com/go-chi/chi"
+ "github.com/stakwork/sphinx-tribes/auth"
+ "github.com/stakwork/sphinx-tribes/db"
+ "github.com/stakwork/sphinx-tribes/handlers"
+)
+
+func FeatureRoutes() chi.Router {
+ r := chi.NewRouter()
+ featureHandlers := handlers.NewFeatureHandler(db.DB)
+ r.Group(func(r chi.Router) {
+ r.Use(auth.PubKeyContext)
+
+ r.Post("/", featureHandlers.CreateOrEditFeatures)
+ r.Get("/forworkspace/{uuid}", featureHandlers.GetFeaturesByWorkspaceUuid)
+ r.Get("/{uuid}", featureHandlers.GetFeatureByUuid)
+ })
+ return r
+}
diff --git a/routes/index.go b/routes/index.go
index 98dc0c162..22090938c 100644
--- a/routes/index.go
+++ b/routes/index.go
@@ -36,6 +36,7 @@ func NewRouter() *http.Server {
r.Mount("/gobounties", BountyRoutes())
r.Mount("/workspaces", WorkspaceRoutes())
r.Mount("/metrics", MetricsRoutes())
+ r.Mount("/features", FeatureRoutes())
r.Group(func(r chi.Router) {
r.Get("/tribe_by_feed", tribeHandlers.GetFirstTribeByFeed)
diff --git a/routes/metrics.go b/routes/metrics.go
index e24a1386d..19065e175 100644
--- a/routes/metrics.go
+++ b/routes/metrics.go
@@ -13,6 +13,8 @@ func MetricsRoutes() chi.Router {
r.Group(func(r chi.Router) {
r.Use(auth.PubKeyContextSuperAdmin)
+ r.Get("/workspaces", handlers.GetAdminWorkspaces)
+
r.Post("/payment", handlers.PaymentMetrics)
r.Post("/people", handlers.PeopleMetrics)
r.Post("/organization", handlers.WorkspacetMetrics)