Home Reference Source Test Repository

src/controller/identity.js

  1. import NPROneSDK from './../index';
  2. import FetchUtil from './../util/fetch-util';
  3. import User from './../model/user';
  4. import Logger from './../util/logger';
  5. import AccessToken from './../model/access-token';
  6. import StationFinder from './station-finder';
  7.  
  8.  
  9. /**
  10. * Encapsulates all of the logic for communication with the [Identity Service](https://dev.npr.org/api/#/identity)
  11. * in the NPR One API.
  12. *
  13. * Note that consumers should not be accessing this class directly but should instead use the provided pass-through
  14. * functions in the main {@link NprOneSDK} class.
  15. *
  16. * @example <caption>How to change a user's station using station search</caption>
  17. * const nprOneSDK = new NprOneSDK();
  18. * nprOneSDK.config = { ... };
  19. * nprOneSDK.getUser() // optional; verifies that you have a logged-in user
  20. * .then(() => {
  21. * return nprOneSDK.searchStations('wnyc');
  22. * })
  23. * .then((stations) => {
  24. * const stationId = stations[0].id; // in reality, you'd probably have the user select a station, see the StationFinder for detail
  25. * nprOneSDK.setUserStation(stationId);
  26. * });
  27. */
  28. export default class Identity {
  29. /**
  30. * Gets user metadata, such as first and last name, programs they have shown an affinity for, and preferred NPR One
  31. * station.
  32. *
  33. * @returns {Promise<User>}
  34. */
  35. getUser() {
  36. const url = `${NPROneSDK.getServiceUrl('identity')}/user`;
  37.  
  38. return FetchUtil.nprApiFetch(url).then(json => new User(json));
  39. }
  40.  
  41. /**
  42. * Sets a user's favorite NPR station. Note that this function will first validate whether the station with the given
  43. * ID actually exists, and will return a promise that rejects if not.
  44. *
  45. * @param {number|string} stationId The station's ID, which is either an integer or a numeric string (e.g. `123` or `'123'`)
  46. * @returns {Promise<User>}
  47. */
  48. setUserStation(stationId) {
  49. return StationFinder.validateStation(stationId)
  50. .then(() => {
  51. const url = `${NPROneSDK.getServiceUrl('identity')}/stations`;
  52. const options = {
  53. method: 'PUT',
  54. body: JSON.stringify([stationId]),
  55. };
  56. return FetchUtil.nprApiFetch(url, options).then(json => new User(json));
  57. })
  58. .catch((e) => {
  59. Logger.debug('setUserStation failed, message: ', e);
  60. return Promise.reject(e);
  61. });
  62. }
  63.  
  64. /**
  65. * Indicates that the user wishes to follow, or subscribe to, the show, program, or podcast with the given numeric
  66. * ID. Followed shows will appear more frequently in a user's list of recommendations.
  67. *
  68. * Note that at this time, because we have not yet implemented search in this SDK, there is no way to retrieve a list
  69. * of aggregation (show) IDs through this SDK. You can either add functionality to your own app that makes an API call
  70. * to `GET https://api.npr.org/listening/v2/search/recommendations` with a program name or other search parameters, or
  71. * wait until we implement search in this SDK (hopefully later this year).
  72. *
  73. * @param {number|string} aggregationId The aggregation (show) ID, which is either an integer or a numeric string (e.g. `123` or `'123'`)
  74. * @returns {Promise<User>}
  75. * @throws {TypeError} if the passed-in aggregation (show) ID is not either a number or a numeric string
  76. */
  77. followShow(aggregationId) {
  78. return this._setFollowingStatusForShow(aggregationId, true);
  79. }
  80.  
  81. /**
  82. * Indicates that the user wishes to unfollow, or unsubscribe from, the show, program, or podcast with the given
  83. * numeric ID. See {@link followShow} for more information.
  84. *
  85. * @param {number|string} aggregationId The aggregation (show) ID, which is either an integer or a numeric string (e.g. `123` or `'123'`)
  86. * @returns {Promise<User>}
  87. * @throws {TypeError} if the passed-in aggregation (show) ID is not either a number or a numeric string
  88. */
  89. unfollowShow(aggregationId) {
  90. return this._setFollowingStatusForShow(aggregationId, false);
  91. }
  92.  
  93. /**
  94. * Primary workhorse for {@link followShow} and {@link unfollowShow}.
  95. *
  96. * @param {number|string} aggregationId The aggregation (show) ID, which is either an integer or a numeric string (e.g. `123` or `'123'`)
  97. * @param {boolean} shouldFollow Whether or not the aggregation should be followed (`true`) or unfollowed (`false`)
  98. * @returns {Promise<User>}
  99. * @throws {TypeError} if the passed-in aggregation (show) ID is not either a number or a numeric string
  100. * @private
  101. */
  102. _setFollowingStatusForShow(aggregationId, shouldFollow) {
  103. const n = parseInt(aggregationId, 10);
  104. if (isNaN(n) || !isFinite(n)) {
  105. throw new TypeError('Aggregation (show) ID must be an integer greater than 0');
  106. }
  107.  
  108. const data = {
  109. id: aggregationId,
  110. following: shouldFollow,
  111. };
  112.  
  113. const url = `${NPROneSDK.getServiceUrl('identity')}/following`;
  114. const options = {
  115. method: 'POST',
  116. body: JSON.stringify(data),
  117. };
  118.  
  119. return FetchUtil.nprApiFetch(url, options).then(json => new User(json));
  120. }
  121.  
  122. /**
  123. * Creates a temporary user from the NPR One API and use that user's access token for
  124. * subsequent API requests.
  125. *
  126. * Caution: most clients are not authorized to use temporary users.
  127. *
  128. * @returns {Promise<User>}
  129. * @throws {TypeError} if an OAuth proxy is not configured or no client ID is set
  130. */
  131. createTemporaryUser() {
  132. if (!NPROneSDK.config.authProxyBaseUrl) {
  133. throw new TypeError('OAuth proxy not configured. Unable to create temporary users.');
  134. }
  135. if (!NPROneSDK.config.clientId) {
  136. throw new TypeError('A client ID must be set for temporary user requests.');
  137. }
  138.  
  139. let url = `${NPROneSDK.config.authProxyBaseUrl}${NPROneSDK.config.tempUserPath}`;
  140. const glueCharacter = url.indexOf('?') >= 0 ? '&' : '?';
  141. url = `${url}${glueCharacter}clientId=${NPROneSDK.config.clientId}`;
  142.  
  143. const options = {
  144. credentials: 'include',
  145. };
  146.  
  147. return FetchUtil.nprApiFetch(url, options)
  148. .then((json) => {
  149. const tokenModel = new AccessToken(json);
  150. tokenModel.validate(); // throws exception if invalid
  151. NPROneSDK.accessToken = tokenModel.token;
  152. return tokenModel; // never directly consumed, but useful for testing
  153. });
  154. }
  155. }