Skip to content

Commit 2f08a80

Browse files
committed
Add org teams
* add method / test to create teams as part of orgs * add option to team.list to filter by organization
1 parent a326c08 commit 2f08a80

File tree

4 files changed

+76
-8
lines changed

4 files changed

+76
-8
lines changed

app/db/migrations/20200326113917_organization.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,17 @@ exports.up = async (knex) => {
1919
table.integer('osm_id')
2020
table.unique(['organization_id', 'osm_id'])
2121
})
22+
23+
await knex.schema.createTable('organization_team', table => {
24+
table.increments('id')
25+
table.integer('team_id').references('id').inTable('team').onDelete('CASCADE')
26+
table.integer('organization_id').references('id').inTable('organization').onDelete('CASCADE')
27+
table.unique(['organization_id', 'team_id'])
28+
})
2229
}
2330

2431
exports.down = async (knex) => {
32+
await knex.schema.dropTable('organization_team')
2533
await knex.schema.dropTable('organization_manager')
2634
await knex.schema.dropTable('organization_owner')
2735
await knex.schema.dropTable('organization')

app/lib/organization.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const db = require('../db')
2+
const team = require('./team')
23
const { map, prop, contains } = require('ramda')
34
const { unpack } = require('./utils')
45

@@ -147,6 +148,26 @@ async function removeManager (id, osmId) {
147148
return unpack(conn('organization_manager').where({ organization_id: id, osm_id: osmId }).del())
148149
}
149150

151+
/**
152+
* Create organization team
153+
*
154+
* An organization team is a team that is assigned to the organization
155+
* at creation time. Only organization managers can create teams
156+
*
157+
* @param {int} organizationId - organization id
158+
* @param {object} data - params for team (see team.create function)
159+
* @param {int} osmId - id of the organization manager
160+
* @return {promise}
161+
*/
162+
async function createOrgTeam (organizationId, data, osmId) {
163+
const conn = await db()
164+
165+
return conn.transaction(async trx => {
166+
const record = await team.create(data, osmId, trx)
167+
await trx('organization_team').insert({ team_id: record.id, organization_id: organizationId })
168+
})
169+
}
170+
150171
/**
151172
* Checks if the osm user is an owner of a team
152173
* @param {int} organizationId - organization id
@@ -187,5 +208,6 @@ module.exports = {
187208
getOwners,
188209
getManagers,
189210
isOwner,
190-
isManager
211+
isManager,
212+
createOrgTeam
191213
}

app/lib/team.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,13 @@ async function getModerators (id) {
8080
*
8181
* @param options
8282
* @param {int} options.osmId - filter by whether osmId is a member
83+
* @param {int} options.organizationId - filter by whether team belongs to organization
8384
* @param {Array[float]} options.bbox - filter for teams whose location is in bbox (xmin, ymin, xmax, ymax)
8485
* @return {Promise[Array]}
8586
**/
8687
async function list (options) {
8788
options = options || {}
88-
const { osmId, bbox } = options
89+
const { osmId, bbox, organizationId } = options
8990

9091
const conn = await db()
9192
const st = knexPostgis(conn)
@@ -98,6 +99,12 @@ async function list (options) {
9899
})
99100
}
100101

102+
if (organizationId) {
103+
query = query.whereIn('id', function () {
104+
this.select('team_id').from('organization_team').where('organization_id', organizationId)
105+
})
106+
}
107+
101108
if (bbox) {
102109
query = query.where(st.boundingBoxContained('location', st.makeEnvelope(...bbox)))
103110
}
@@ -129,12 +136,14 @@ async function listModeratedBy (osmId) {
129136
* @param {string} data.name - name of the team
130137
* @param {geojson} data.location - lat/lon of team
131138
* @param {int} osmId - id of first moderator
139+
* @param {object} conn - optional parameter for database connection to re-use connection in case of nested
140+
* transactions. This is used when a team is created as part of an organization
132141
* @return {promise}
133142
**/
134-
async function create (data, osmId) {
143+
async function create (data, osmId, conn) {
135144
if (!osmId) throw new Error('moderator osm id is required as second argument')
136145
if (!data.name) throw new Error('data.name property is required')
137-
const conn = await db()
146+
conn = conn || await db()
138147
const st = knexPostgis(conn)
139148

140149
// convert location to postgis geom

app/tests/api/organization-model.test.js

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const test = require('ava')
33
const { prop, map, contains } = require('ramda')
44
const db = require('../../db')
55
const organization = require('../../lib/organization')
6+
const team = require('../../lib/team')
67

78
const migrationsDirectory = path.join(__dirname, '..', '..', 'db', 'migrations')
89

@@ -91,8 +92,8 @@ test('update an organization', async t => {
9192
const newName = 'update test - new name'
9293
const user = 1
9394
const created = await organization.create({ name }, user)
94-
const updated = await organization.update(created.id, { name: newName },)
95-
95+
const updated = await organization.update(created.id, { name: newName })
96+
9697
// tests
9798
t.is(updated.name, newName)
9899

@@ -102,7 +103,8 @@ test('update an organization', async t => {
102103
})
103104

104105
/**
105-
* Test add an owner
106+
* Test adding an owner
107+
* - After adding an owner the number of owners increases by 1
106108
*/
107109
test('add owners', async t => {
108110
// setup
@@ -112,7 +114,6 @@ test('add owners', async t => {
112114
const created = await organization.create({ name }, user)
113115
await organization.addOwner(created.id, user2)
114116

115-
116117
// tests
117118
const owners = map(prop('osm_id'), await organization.getOwners(created.id))
118119
t.is(owners.length, 2)
@@ -123,6 +124,12 @@ test('add owners', async t => {
123124
await t.throwsAsync(organization.addOwner(created.id, user2))
124125
})
125126

127+
/**
128+
* Test removing owners
129+
* - After adding and removing an owner the number of owners should remain the same
130+
* - Removing a user that is not an owner throws an error
131+
* - Trying to remove the last owner throws an error
132+
*/
126133
test('remove owners', async t => {
127134
// setup
128135
const name = 'remove owners'
@@ -171,6 +178,11 @@ test('add managers', async t => {
171178
await t.throwsAsync(organization.addManager(created.id, user2))
172179
})
173180

181+
/**
182+
* Test removing managers
183+
* - After adding and removing a manager the number of owners should remain the same
184+
* - Removing a user that is not a manager throws an error
185+
*/
174186
test('remove managers', async t => {
175187
// setup
176188
const name = 'remove managers'
@@ -191,3 +203,20 @@ test('remove managers', async t => {
191203
const error = await t.throwsAsync(organization.removeManager(created.id, user3))
192204
t.is(error.message, 'osmId is not a manager')
193205
})
206+
207+
/**
208+
* Test creating a team as part of an organization
209+
*/
210+
test('create an organization team', async t => {
211+
// setup
212+
const orgName = 'organization team'
213+
const teamName = 'org team 1'
214+
const user = 1
215+
const org = await organization.create({ name: orgName }, user)
216+
await organization.createOrgTeam(org.id, { name: teamName }, user)
217+
218+
// tests
219+
const teams = await team.list({ organizationId: org.id })
220+
t.is(teams.length, 1)
221+
t.is(teams[0].name, teamName)
222+
})

0 commit comments

Comments
 (0)