UNPKG

ltijs

Version:

Easily turn your web application into a LTI 1.3 Learning Tool.

127 lines • 6.44 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.NamesAndRoles = void 0; const link_header_1 = require("#utils/request/link-header"); const authorization_header_1 = require("#utils/request/authorization-header"); const validation_1 = require("#utils/validation/validation"); const id_token_constants_1 = require("#services/launch/id-token.constants"); const lti_scopes_constants_1 = require("#shared/lti-scopes.constants"); const names_and_roles_schemas_1 = require("#services/names-and-roles/names-and-roles.schemas"); const names_and_roles_serializer_1 = require("#services/names-and-roles/names-and-roles.serializer"); const errors_1 = require("#services/names-and-roles/errors"); const errors_2 = require("#shared/errors"); class NamesAndRoles { LOG_COMPONENT = 'namesAndRolesService'; NRPS_ACCEPT = 'application/vnd.ims.lti-nrps.v2.membershipcontainer+json'; ROLE_PARAM = 'role'; LIMIT_PARAM = 'limit'; RESOURCE_LINK_ID_PARAM = 'rlid'; launchContext; accessTokenManager; requestHandler; logger; constructor(launchContext, accessTokenManager, requestHandler, logger) { this.launchContext = launchContext; this.accessTokenManager = accessTokenManager; this.requestHandler = requestHandler; this.logger = logger; } /** Whether this launch declared NRPS support. Check before calling any other method on this service. */ isAvailable() { return this.launchContext.idToken.services.namesAndRoles.available; } async getMembers(options) { this.ensureServiceAvailability(); const { platform, rawIdToken: idToken } = this.launchContext; const request = this.buildMembershipsRequest(idToken, options); this.logger.debug(this.LOG_COMPONENT, 'Attempting to retrieve memberships'); this.logger.debug(this.LOG_COMPONENT, 'Target platform: ' + idToken.iss); const accessToken = await this.accessTokenManager.getAccessToken(platform, lti_scopes_constants_1.NRPS_CONTEXT_MEMBERSHIP_READONLY_SCOPE); this.logger.debug(this.LOG_COMPONENT, 'Access_token retrieved for [' + idToken.iss + ']'); const result = await this.fetchPages(request, accessToken, this.resolveNumberOfPages(options?.pages)); return (0, names_and_roles_serializer_1.buildMemberships)(result); } ensureServiceAvailability() { if (!this.isAvailable()) throw new errors_1.NamesAndRolesNotAvailableError(); } resolveNumberOfPages(pages) { if (pages === false) return false; return pages === undefined || pages < 1 ? 1 : pages; } buildMembershipsRequest(idToken, options) { const namesRoles = idToken[id_token_constants_1.IdTokenClaim.NamesRoleService]; const namesRolesUrl = namesRoles?.context_memberships_url; if (namesRolesUrl === undefined) throw new errors_1.MissingNamesRolesServiceUrlError(); const pageUrl = namesRolesUrl; const query = new URLSearchParams(); if (options === undefined) return { pageUrl, query }; if (options.url !== undefined && options.url !== '') return { pageUrl: options.url, query }; if (options.role !== undefined && options.role !== '') { this.logger.debug(this.LOG_COMPONENT, 'Adding role parameter with value: ' + options.role); query.append(this.ROLE_PARAM, options.role); } if (options.limit !== undefined && options.limit > 0) { this.logger.debug(this.LOG_COMPONENT, 'Adding limit parameter with value: ' + String(options.limit)); query.append(this.LIMIT_PARAM, String(options.limit)); } if (options.resourceLinkId !== undefined && options.resourceLinkId !== false && options.resourceLinkId !== '') { const resourceLinkId = options.resourceLinkId === true ? idToken[id_token_constants_1.IdTokenClaim.ResourceLink]?.id : options.resourceLinkId; if (resourceLinkId === undefined || resourceLinkId === '') throw new errors_2.MissingOrInvalidResourceLinkIdError(); this.logger.debug(this.LOG_COMPONENT, 'Adding rlid parameter with value: ' + resourceLinkId); query.append(this.RESOURCE_LINK_ID_PARAM, resourceLinkId); } return { pageUrl, query }; } async fetchPages(request, accessToken, numberOfPages) { let pageUrl = request.pageUrl; let differencesUrl; let result; let pageNumber = 1; do { if (numberOfPages !== false && pageNumber > numberOfPages) { if (result !== undefined) result.next = pageUrl; break; } this.logger.debug(this.LOG_COMPONENT, `Fetching membership page ${pageNumber}: ${pageUrl}`); const page = await this.fetchPage(pageUrl, pageNumber === 1 ? request.query : undefined, accessToken); if (result === undefined) result = page.memberships; else result.members = [...result.members, ...page.memberships.members]; if (page.differences !== undefined) differencesUrl = page.differences; pageUrl = page.next; pageNumber++; } while (pageUrl !== undefined); if (result === undefined) throw new errors_1.MembersNotFoundError(); if (differencesUrl !== undefined) result.differences = differencesUrl; this.logger.debug(this.LOG_COMPONENT, 'Memberships retrieved'); return result; } async fetchPage(pageUrl, query, accessToken) { const headers = { authorization: (0, authorization_header_1.buildBearerAuthorization)(accessToken), accept: this.NRPS_ACCEPT, }; const response = await this.requestHandler.get(pageUrl, { query, headers }); const memberships = (0, validation_1.validate)(names_and_roles_schemas_1.MembershipContainerSchema, response.data); const parsedLinks = (0, link_header_1.parseLinkHeader)(response.headers.link); return { memberships, next: parsedLinks?.next?.url, differences: parsedLinks?.differences?.url, }; } } exports.NamesAndRoles = NamesAndRoles; exports.default = NamesAndRoles; //# sourceMappingURL=names-and-roles.service.js.map