Skip to content

Commit

Permalink
feat(NamesAndRoles): Added the capability to remove page limitation
Browse files Browse the repository at this point in the history
  • Loading branch information
Cvmcosta committed Jan 29, 2021
1 parent 6ec0fec commit 1cd02ec
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 17 deletions.
14 changes: 7 additions & 7 deletions dist/Provider/Services/NamesAndRoles.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class NamesAndRoles {
* @param {Object} options - Request options.
* @param {String} [options.role] - Filters based on the User role.
* @param {Number} [options.limit] - Sets a maximum number of memberships to be returned per page.
* @param {Number} [options.pages = 1] - Sets a maximum number of pages to be returned. Defaults to 1.
* @param {Number} [options.pages = 1] - Sets a maximum number of pages to be returned. Defaults to 1. If set to false retrieves every available page.
* @param {String} [options.url] - Retrieve memberships from a specific URL. Usually retrieved from the `next` link header of a previous request.
* @param {Boolean} [options.resourceLinkId = false] - If set to true, retrieves resource Link level memberships.
*/
Expand Down Expand Up @@ -78,6 +78,11 @@ class NamesAndRoles {
let next = idtoken.platformContext.namesRoles.context_memberships_url;

if (options) {
if (options.pages || options.pages === false) {
provNamesAndRolesServiceDebug('Maximum number of pages retrieved: ' + options.pages);
pages = options.pages;
}

if (options.url) {
next = options.url;
query = false;
Expand All @@ -96,11 +101,6 @@ class NamesAndRoles {
provNamesAndRolesServiceDebug('Adding rlid parameter with value: ' + idtoken.platformContext.resource.id);
query.push(['rlid', idtoken.platformContext.resource.id]);
}

if (options.pages) {
provNamesAndRolesServiceDebug('Maximum number of pages retrieved: ' + options.pages);
pages = options.pages;
}
}
}

Expand All @@ -110,7 +110,7 @@ class NamesAndRoles {
let curPage = 1;

do {
if (curPage > pages) {
if (pages && curPage > pages) {
if (next) result.next = next;
break;
}
Expand Down
5 changes: 5 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@

### CHANGELOG

#### V5.6.6
> 2021-01-29
> - Added the capability to remove the limit of number of pages returned by the Names and Roles Service.

#### V5.6.5
> 2021-01-27
> - Fixed bug where Blackboard would not see access token request iat and exp claims.
Expand Down
4 changes: 2 additions & 2 deletions docs/namesandroles.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ The `getMembers()` method allows us to apply filters to the request, and these f
Specifies the number of members per page that should be returned per members page **(By default only one members page is returned.)**.

- **options.pages**
Specifies the number of pages that should be returned. *Defaults to 1*.
Specifies the number of pages that should be returned. *Defaults to 1*. If set to false retrieves every available page.

Ex:

Expand Down Expand Up @@ -212,7 +212,7 @@ Retrieves members from platform.
| options | `Object` | Options object. |
| options.role| `String` | Specific role to be returned. |
| options.limit | `Number` | Specifies maximum number of members per page. |
| options.pages | `Number` | Specifies maximum number of pages returned. Defaults to 1. |
| options.pages | `Number` | Specifies maximum number of pages returned. Defaults to 1. If set to false retrieves every available page. |
| options.url | `String` | Specifies the initial members endpoint, usually retrieved from a previous incomplete request. |
| options.resourceLinkId | `Boolean` | If set to true, retrieves resource Link level memberships. |

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ltijs",
"version": "5.6.5",
"version": "5.6.6",
"description": "Easily turn your web application into a LTI 1.3 Learning Tool.",
"main": "index.js",
"engineStrict": true,
Expand Down
12 changes: 6 additions & 6 deletions src/Provider/Services/NamesAndRoles.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class NamesAndRoles {
* @param {Object} options - Request options.
* @param {String} [options.role] - Filters based on the User role.
* @param {Number} [options.limit] - Sets a maximum number of memberships to be returned per page.
* @param {Number} [options.pages = 1] - Sets a maximum number of pages to be returned. Defaults to 1.
* @param {Number} [options.pages = 1] - Sets a maximum number of pages to be returned. Defaults to 1. If set to false retrieves every available page.
* @param {String} [options.url] - Retrieve memberships from a specific URL. Usually retrieved from the `next` link header of a previous request.
* @param {Boolean} [options.resourceLinkId = false] - If set to true, retrieves resource Link level memberships.
*/
Expand All @@ -50,6 +50,10 @@ class NamesAndRoles {

let next = idtoken.platformContext.namesRoles.context_memberships_url
if (options) {
if (options.pages || options.pages === false) {
provNamesAndRolesServiceDebug('Maximum number of pages retrieved: ' + options.pages)
pages = options.pages
}
if (options.url) {
next = options.url
query = false
Expand All @@ -66,10 +70,6 @@ class NamesAndRoles {
provNamesAndRolesServiceDebug('Adding rlid parameter with value: ' + idtoken.platformContext.resource.id)
query.push(['rlid', idtoken.platformContext.resource.id])
}
if (options.pages) {
provNamesAndRolesServiceDebug('Maximum number of pages retrieved: ' + options.pages)
pages = options.pages
}
}
}

Expand All @@ -81,7 +81,7 @@ class NamesAndRoles {
let curPage = 1

do {
if (curPage > pages) {
if (pages && curPage > pages) {
if (next) result.next = next
break
}
Expand Down
47 changes: 46 additions & 1 deletion test/4-namesandroles.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ describe('Testing Names and Roles Service', function () {

lti.onConnect(async (token, req, res) => {
try {
return res.send(await lti.NamesAndRoles.getMembers(token, { role: 'Learner', limit: 2, pages: 1 }))
return res.send(await lti.NamesAndRoles.getMembers(token, { role: 'Learner', limit: 2 }))
} catch (err) {
res.sendStatus(500)
}
Expand Down Expand Up @@ -184,6 +184,51 @@ describe('Testing Names and Roles Service', function () {
expect(result.members[3]).to.deep.include(membersResult.members[1])
})
})

it('NamesAndRoles.getMembers() expected to return valid member list and include multiple pages when "pages" parameter is set to false', async () => {
const token = JSON.parse(JSON.stringify(tokenValid))
token.nonce = encodeURIComponent([...Array(25)].map(_ => (Math.random() * 36 | 0).toString(36)).join``)

const payload = signToken(token, '123456')
const state = encodeURIComponent([...Array(25)].map(_ => (Math.random() * 36 | 0).toString(36)).join``)
const url = await lti.appRoute()

nock('http://localhost/moodle').get('/keyset').reply(200, {
keys: [
{ kty: 'RSA', e: 'AQAB', kid: '123456', n: 'VrJSr-xli8NfuAdk_Wem5BARmmW4BpJvXBx3MbFY_0grH9Cd7OxBwVYSwI4P4yhL27upa1_FCRwLi3raOPSJOkHEDvFwtyYZMvdYcpDYTv6JRVqbgEyZtHa-vjL1wBqqW75yPDRoyZdnA8MWrfyRUOak53ZVWHRKgBnP53oXm7M' }
]
})

nock('http://localhost/moodle').post('/AccessTokenUrl').reply(200, {
access_token: 'dkj4985kjaIAJDJ89kl8rkn5',
token_type: 'bearer',
expires_in: 3600,
scope: 'https://purl.imsglobal.org/spec/lti-nrps/scope/contextmembership.readonly'
})

nock('http://localhost/moodle').get('/members?role=Learner&limit=2').reply(200, membersResult, { link: '<http://localhost/moodle/api/lti/courses/1/names_and_roles?since=623698163>; rel="differences",<http://localhost/moodle/api/lti/courses/1/names_and_roles?page=2&per_page=1>; rel="next",<http://localhost/moodle/api/lti/courses/1/names_and_roles?page=1&per_page=1>; rel="first",<http://localhost/moodle/api/lti/courses/1/names_and_roles?page=2&per_page=1>; rel="last"' })

nock('http://localhost/moodle').get('/api/lti/courses/1/names_and_roles?page=2&per_page=1').reply(200, membersResult)

lti.onConnect(async (token, req, res) => {
try {
return res.send(await lti.NamesAndRoles.getMembers(token, { role: 'Learner', limit: 2, pages: false }))
} catch (err) {
console.log(err)
res.sendStatus(500)
}
})

return chai.request(lti.app).post(url).type('json').send({ id_token: payload, state: state }).set('Cookie', ['state' + state + '=s%3Ahttp%3A%2F%2Flocalhost%2Fmoodle.fsJogjTuxtbJwvJcuG4esveQAlih67sfEltuwRM6MX0; Path=/; HttpOnly;', 'ltiaHR0cDovL2xvY2FsaG9zdC9tb29kbGVDbGllbnRJZDEy=s%3A2.ZezwPKtv3Uibp4A%2F6cN0UzbIQlhA%2BTAKvbtN%2FvgGaCI; Path=/; HttpOnly; SameSite=None']).then(res => {
expect(res).to.have.status(200)

const result = JSON.parse(res.text)
expect(result.differences).to.eql('http://localhost/moodle/api/lti/courses/1/names_and_roles?since=623698163')
expect(result.members[0]).to.deep.include(membersResult.members[0])
expect(result.members[3]).to.deep.include(membersResult.members[1])
})
})

it('NamesAndRoles.getMembers() expected to return valid member list when using custom url', async () => {
const token = JSON.parse(JSON.stringify(tokenValid))
token.nonce = encodeURIComponent([...Array(25)].map(_ => (Math.random() * 36 | 0).toString(36)).join``)
Expand Down

0 comments on commit 1cd02ec

Please sign in to comment.