Skip to content

Commit

Permalink
Merge pull request stakwork#1630 from stakwork/feature-add-features-t…
Browse files Browse the repository at this point in the history
…o-workspaces

Feature add features to workspaces
  • Loading branch information
elraphty authored May 3, 2024
2 parents e6c08b3 + 3c8fdc1 commit 12ee59e
Show file tree
Hide file tree
Showing 10 changed files with 525 additions and 31 deletions.
158 changes: 158 additions & 0 deletions cypress/e2e/03_features.cy.ts
Original file line number Diff line number Diff line change
@@ -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")
})
}
})
})
})
72 changes: 41 additions & 31 deletions cypress/support/objects/objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,78 +57,88 @@ export const Workspaces = [

export const Repositories = [
{
name: 'frontend',
url: 'https://github.com/stakwork/sphinx-tribes-frontend'
uuid: 'com1t3gn1e4a4qu3tnlg',
workspace_uuid: 'cohob00n1e4808utqel0',
name: ' frontend ',
url: ' https://github.com/stakwork/sphinx-tribes-frontend '
},
{
name: 'backend',
url: 'https://github.com/stakwork/sphinx-tribes'
uuid: 'com1t3gn1e4a4qu3tnlg',
workspace_uuid: 'cohob00n1e4808utqel0',
name: ' backend ',
url: ' https://github.com/stakwork/sphinx-tribes '
}
];

export const Features = [
{
name: 'Hive Process',
uuid: 'com1kson1e49th88dbg0',
workspace_uuid: 'cohob00n1e4808utqel0',
name: ' Hive Process ',
priority: 1,
brief: 'To follow a set of best practices in product development.</br>' +
brief: ' To follow a set of best practices in product development.</br>' +
'Dividing complex features into small<br>steps makes it easier to ' +
'track and the timing more certain.<br/>A guided process would help ' +
'a PM new to the hive process get the best results with the least mental ' +
'load.<br/>This feature is for a not se technical Product Manager.<br/>' +
'The hive process lets you get features out to production faster and with less risk.',
requirements: 'Modify workspaces endpoint to accomodate new fields.<br/>' +
'Create end points for features, user stories and phases',
architecture: 'Describe the architecture of the feature with the following sections:' +
'The hive process lets you get features out to production faster and with less risk. ',
requirements: ' Modify workspaces endpoint to accomodate new fields.<br/>' +
'Create end points for features, user stories and phases ',
architecture: ' Describe the architecture of the feature with the following sections:' +
'<br/><br/>Wireframes<br/><br/>Visual Schematics<br/><br/>Object Definition<br/><br/>' +
'DB Schema Changes<br/><br/>UX<br/><br/>CI/CD<br/><br/>Changes<br/><br/>Endpoints<br/><br/>' +
'Front<br/><br/>',
'Front<br/><br/> ',
},
{
name: 'AI Assited text fields',
uuid: 'com1l5on1e49tucv350g',
workspace_uuid: 'cohob00n1e4808utqel0',
name: ' AI Assited text fields ',
priority: 2,
brief: 'An important struggle of a technical product manager is to find ' +
'the right words to describe a business goal. The definition of ' +
'things like \'product mission\' or \'tactics and objectives\' is ' +
' the base from which every technical decition relays on.<br/>' +
'We are going to leverage AI to help the PM write better definitions.<br/>' +
'The fields that would benefit form AI assistance are: mission, tactics, ' +
'feature brief and feature user stories',
requirements: 'Create a new page for a conversation format between the PM and the LLM<br/>' +
'feature brief and feature user stories ',
requirements: ' Create a new page for a conversation format between the PM and the LLM<br/>' +
'Rely as much as possible on stakwork workflows<br/>' +
'Have history of previous definitions',
architecture: 'Describe the architecture of the feature with the following sections:' +
'Have history of previous definitions ',
architecture: ' Describe the architecture of the feature with the following sections:' +
'<br/><br/>Wireframes<br/><br/>Visual Schematics<br/><br/>Object Definition<br/><br/>' +
'DB Schema Changes<br/><br/>UX<br/><br/>CI/CD<br/><br/>Changes<br/><br/>Endpoints<br/><br/>' +
'Front<br/><br/>',
'Front<br/><br/> ',
},
{
name: 'AI Assited relation between text fields',
uuid: 'com1l5on1e49tucv350h',
workspace_uuid: 'cohob00n1e4808utqel0',
name: ' AI Assited relation between text fields ',
priority: 2,
brief: 'A product and feature\'s various definition fields: mission, tactics, ' +
brief: ' A product and feature\'s various definition fields: mission, tactics, ' +
'feature brief, user stories, requirements and architecture should have some ' +
'relation between each other.<br/>' + 'One way to do that is to leverage an LLM ' +
'to discern the parts of the defintion that have a connection to other definitions.<br/>' +
'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:' +
'<br/><br/>Wireframes<br/><br/>Visual Schematics<br/><br/>Object Definition<br/><br/>' +
'DB Schema Changes<br/><br/>UX<br/><br/>CI/CD<br/><br/>Changes<br/><br/>Endpoints<br/><br/>' +
'Front<br/><br/>',
'Front<br/><br/> ',
},
];

export const UserStories = [
{ id: 'f4c4c4b4-7a90-4a3a-b3e2-151d0feca9bf', description: ' As a {PM} I want to {make providers \"hive ready\"}, so I can {leverage the hive process ' },
{ id: '78f4b326-1841-449b-809a-a0947622db3e', description: ' As a {PM} I want to {CRUD Features}, so I can {use the system to manage my features} ' },
{ id: '5d353d23-3d27-4aa8-a9f7-04dcd5f4843c', description: ' As a {PM} I want to {follow best practices}, so I can {make more valuable features} ' },
{ id: '1a4e00f4-0e58-4e08-a1df-b623bc10f08d', description: ' As a {PM} I want to {save the architecture of the feature}, so I can {share it with people} ' },
{ id: 'eb6e4138-37e5-465d-934e-18e335abaa47', description: ' As a {PM} I want to {create phases}, so I can {divide the work in several deliverable stages} ' },
{ id: '35a5d8dd-240d-4ff0-a699-aa2fa2cfa32c', description: ' As a {PM} I want to {assign bounties to features}, so I can {group bounties together} ' },
{ uuid: 'com1lh0n1e49ug76noig', feature_uuid: 'com1kson1e49th88dbg0', description: ' As a {PM} I want to {make providers \"hive ready\"}, so I can {leverage the hive process ' },
{ uuid: 'com1lk8n1e49uqfe3l40', feature_uuid: 'com1kson1e49th88dbg0', description: ' As a {PM} I want to {CRUD Features}, so I can {use the system to manage my features} ' },
{ uuid: 'com1ln8n1e49v4159gug', feature_uuid: 'com1kson1e49th88dbg0', description: ' As a {PM} I want to {follow best practices}, so I can {make more valuable features} ' },
{ uuid: 'com1lqgn1e49vevhs9k0', feature_uuid: 'com1kson1e49th88dbg0', description: ' As a {PM} I want to {save the architecture of the feature}, so I can {share it with people} ' },
{ uuid: 'com1lt8n1e49voquoq90', feature_uuid: 'com1kson1e49th88dbg0', description: ' As a {PM} I want to {create phases}, so I can {divide the work in several deliverable stages} ' },
{ uuid: 'com1m08n1e4a02r6j0pg', feature_uuid: 'com1kson1e49th88dbg0', description: ' As a {PM} I want to {assign bounties to features}, so I can {group bounties together} ' },
];

export const Phases = [
{ id: 'a96e3bff-e5c8-429e-bd65-911d619761aa', name: ' MVP ' },
{ id: '6de147ab-695c-45b1-81e7-2d1a5ba482ab', name: ' MVP ' },
{ id: '28541c4a-41de-447e-86d8-293583d1abc2', name: ' MVP ' },
{ uuid: 'com1msgn1e4a0ts5kls0', feature_uuid: 'com1kson1e49th88dbg0', name: ' MVP ' },
{ uuid: 'com1mvgn1e4a1879uiv0', feature_uuid: 'com1kson1e49th88dbg0', name: ' Phase 2 ' },
{ uuid: 'com1n2gn1e4a1i8p60p0', feature_uuid: 'com1kson1e49th88dbg0', name: ' Phase 3 ' },
];
1 change: 1 addition & 0 deletions db/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ func InitDB() {
db.AutoMigrate(&ConnectionCodes{})
db.AutoMigrate(&BountyRoles{})
db.AutoMigrate(&UserInvoiceData{})
db.AutoMigrate(&WorkspaceFeatures{})

DB.MigrateTablesWithOrgUuid()
DB.MigrateOrganizationToWorkspace()
Expand Down
41 changes: 41 additions & 0 deletions db/features.go
Original file line number Diff line number Diff line change
@@ -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
}
3 changes: 3 additions & 0 deletions db/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,7 @@ type Database interface {
PersonUniqueNameFromName(name string) (string, error)
ProcessAlerts(p Person)
UserHasAccess(pubKeyFromAuth string, uuid string, role string) bool
CreateOrEditFeature(m WorkspaceFeatures) (WorkspaceFeatures, error)
GetFeaturesByWorkspaceUuid(uuid string) []WorkspaceFeatures
GetFeatureByUuid(uuid string) WorkspaceFeatures
}
14 changes: 14 additions & 0 deletions db/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,20 @@ type WorkspaceUsersData struct {
Person
}

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"`
}
Expand Down
Loading

0 comments on commit 12ee59e

Please sign in to comment.