From 3bc4629fcfeeb4697fa471e1b6c9511ebade8819 Mon Sep 17 00:00:00 2001 From: yuenmichelle1 Date: Thu, 18 Apr 2024 00:38:01 -0500 Subject: [PATCH] Update YourStats to callout to Eras instead of Zoo-stats-graphql (#5963) * update your stats with proper fetch callout to eras. remove unused statsClient * remove importing and stubbing statsClient from Recents and Notifications spec * update stats test * update specs that referenced stats Graphql client * Refactor stats fetch * Refactor tests for YourStats stats request changes * Apply suggestions from code review Co-authored-by: Jim O'Donnell * Refactor fetchDailyStats stubs * Add node-fetch comment --------- Co-authored-by: Mark Bouslog Co-authored-by: Jim O'Donnell --- .../stores/User/Recents/Recents.spec.js | 4 +- packages/app-project/stores/User/User.spec.js | 11 +++-- .../Notifications/Notifications.spec.js | 4 +- .../UserPersonalization.spec.js | 20 ++------ .../UserProjectPreferences.spec.js | 5 +- .../YourStats/YourStats.js | 48 +++++++++++-------- .../YourStats/YourStats.spec.js | 24 +++------- .../test/create-global-document.js | 1 + 8 files changed, 53 insertions(+), 64 deletions(-) diff --git a/packages/app-project/stores/User/Recents/Recents.spec.js b/packages/app-project/stores/User/Recents/Recents.spec.js index 8cf8941969..bad9953cd7 100644 --- a/packages/app-project/stores/User/Recents/Recents.spec.js +++ b/packages/app-project/stores/User/Recents/Recents.spec.js @@ -39,13 +39,13 @@ describe('Stores > Recents', function () { collections: [] } })) - sinon.stub(statsClient, 'request').callsFake(() => Promise.resolve(null)) + sinon.stub(statsClient, 'fetchDailyStats').callsFake(() => Promise.resolve(null)) }) after(function () { console.error.restore() rootStore.client.panoptes.get.restore() - statsClient.request.restore() + statsClient.fetchDailyStats.restore() }) it('should exist', function () { diff --git a/packages/app-project/stores/User/User.spec.js b/packages/app-project/stores/User/User.spec.js index d72ef58317..32ddc03ea3 100644 --- a/packages/app-project/stores/User/User.spec.js +++ b/packages/app-project/stores/User/User.spec.js @@ -3,6 +3,9 @@ import * as client from '@zooniverse/panoptes-js' import { expect } from 'chai' import { when } from 'mobx' import nock from 'nock' +import sinon from 'sinon' + +import { statsClient } from './UserPersonalization/YourStats' import Store from '@stores/Store' @@ -16,6 +19,8 @@ describe('stores > User', function () { let userStore beforeEach(function () { + sinon.stub(statsClient, 'fetchDailyStats') + nock('https://panoptes-staging.zooniverse.org/api') .persist() .get('/users/1/recents') @@ -57,11 +62,6 @@ describe('stores > User', function () { .query(true) .reply(200, { notifications: [] }) - nock('https://graphql-stats.zooniverse.org') - .persist() - .post('/graphql') - .reply(200, { data: { statsCount: [] }}) - rootStore = Store.create({ project: { id: '1', loadingState: asyncStates.success @@ -70,6 +70,7 @@ describe('stores > User', function () { }) afterEach(function () { + statsClient.fetchDailyStats.restore() nock.cleanAll() }) diff --git a/packages/app-project/stores/User/UserPersonalization/Notifications/Notifications.spec.js b/packages/app-project/stores/User/UserPersonalization/Notifications/Notifications.spec.js index c245924e01..069e286db8 100644 --- a/packages/app-project/stores/User/UserPersonalization/Notifications/Notifications.spec.js +++ b/packages/app-project/stores/User/UserPersonalization/Notifications/Notifications.spec.js @@ -19,14 +19,14 @@ describe('Stores > Notifications', function () { } before(function () { - sinon.stub(statsClient, 'request') + sinon.stub(statsClient, 'fetchDailyStats') sinon.stub(sugarClient, 'subscribeTo') sinon.stub(sugarClient, 'on') sinon.stub(sugarClient, 'unsubscribeFrom') }) after(function () { - statsClient.request.restore() + statsClient.fetchDailyStats.restore() sugarClient.subscribeTo.restore() sugarClient.on.restore() sugarClient.unsubscribeFrom.restore() diff --git a/packages/app-project/stores/User/UserPersonalization/UserPersonalization.spec.js b/packages/app-project/stores/User/UserPersonalization/UserPersonalization.spec.js index cc60c87931..e84012f78a 100644 --- a/packages/app-project/stores/User/UserPersonalization/UserPersonalization.spec.js +++ b/packages/app-project/stores/User/UserPersonalization/UserPersonalization.spec.js @@ -56,14 +56,16 @@ describe('Stores > UserPersonalization', function () { rootStore = initStore(true, { project }) sinon.spy(rootStore.client.panoptes, 'get') - sinon.stub(statsClient, 'request').callsFake(() => Promise.resolve({ statsCount: MOCK_DAILY_COUNTS })) + sinon.stub(statsClient, 'fetchDailyStats').callsFake(({ projectId, userId }) => (projectId === '2' && userId === '123') ? + Promise.resolve({ data: MOCK_DAILY_COUNTS }) : + Promise.reject(new Error(`Unable to fetch stats for project ${projectId} and user ${userId}`))) sinon.stub(talkAPI, 'get').callsFake(() => Promise.resolve(undefined)) }) after(function () { console.error.restore() rootStore.client.panoptes.get.restore() - statsClient.request.restore() + statsClient.fetchDailyStats.restore() talkAPI.get.restore() nock.cleanAll() }) @@ -97,19 +99,7 @@ describe('Stores > UserPersonalization', function () { }) it('should trigger the child YourStats node to request user statistics', function () { - const query = `{ - statsCount( - eventType: "classification", - interval: "1 Day", - window: "1 Week", - projectId: "2", - userId: "123" - ){ - period, - count - } - }` - expect(statsClient.request).to.have.been.calledOnceWith(query.replace(/\s+/g, ' ')) + expect(statsClient.fetchDailyStats).to.have.been.calledOnceWith({ projectId: '2', userId: '123' }) }) it('should trigger the child Notifications store to request unread notifications', function () { diff --git a/packages/app-project/stores/User/UserPersonalization/UserProjectPreferences/UserProjectPreferences.spec.js b/packages/app-project/stores/User/UserPersonalization/UserProjectPreferences/UserProjectPreferences.spec.js index 7718a3a0c4..8c4b2f3259 100644 --- a/packages/app-project/stores/User/UserPersonalization/UserProjectPreferences/UserProjectPreferences.spec.js +++ b/packages/app-project/stores/User/UserPersonalization/UserProjectPreferences/UserProjectPreferences.spec.js @@ -10,6 +10,7 @@ import { statsClient } from '../YourStats' import UserProjectPreferences, { Settings } from './UserProjectPreferences' import { expect } from 'chai' + describe('Stores > UserProjectPreferences', function () { const project = { id: '2', @@ -71,12 +72,12 @@ describe('Stores > UserProjectPreferences', function () { } before(function () { - sinon.stub(statsClient, 'request') + sinon.stub(statsClient, 'fetchDailyStats') sinon.stub(talkAPI, 'get').resolves([]) }) after(function () { - statsClient.request.restore() + statsClient.fetchDailyStats.restore() talkAPI.get.restore() }) diff --git a/packages/app-project/stores/User/UserPersonalization/YourStats/YourStats.js b/packages/app-project/stores/User/UserPersonalization/YourStats/YourStats.js index d99436e5fa..6e1aeb2746 100644 --- a/packages/app-project/stores/User/UserPersonalization/YourStats/YourStats.js +++ b/packages/app-project/stores/User/UserPersonalization/YourStats/YourStats.js @@ -1,9 +1,32 @@ import asyncStates from '@zooniverse/async-states' -import { GraphQLClient } from 'graphql-request' import { flow, getRoot, types } from 'mobx-state-tree' import auth from 'panoptes-client/lib/auth' +import { env } from '@zooniverse/panoptes-js' -export const statsClient = new GraphQLClient('https://graphql-stats.zooniverse.org/graphql') +function statsHost(env) { + switch (env) { + case 'production': + return 'https://eras.zooniverse.org' + default: + return 'https://eras-staging.zooniverse.org' + } +} + +export const statsClient = { + async fetchDailyStats({ projectId, userId }) { + const token = await auth.checkBearerToken() + const Authorization = `Bearer ${token}` + const stats = statsHost(env) + const queryParams = new URLSearchParams({ + period: 'day', + project_id: projectId + }).toString() + + const response = await fetch(`${stats}/classifications/users/${userId}?${queryParams}`, { headers: { Authorization } }) + const jsonResponse = await response.json() + return jsonResponse + } +} // https://stackoverflow.com/a/51918448/10951669 function firstDayOfWeek (dateObject, firstDayOfWeekIndex) { @@ -67,25 +90,8 @@ const YourStats = types self.setLoadingState(asyncStates.loading) let dailyCounts try { - const token = yield auth.checkBearerToken() - const Authorization = `Bearer ${token}` - statsClient.setHeaders({ - Authorization - }) - const query = `{ - statsCount( - eventType: "classification", - interval: "1 Day", - window: "1 Week", - projectId: "${project.id}", - userId: "${user.id}" - ){ - period, - count - } - }` - const response = yield statsClient.request(query.replace(/\s+/g, ' ')) - dailyCounts = response.statsCount + const statsData = yield statsClient.fetchDailyStats({ projectId: project.id, userId: user.id }) + dailyCounts = statsData.data self.setLoadingState(asyncStates.success) } catch (error) { self.handleError(error) diff --git a/packages/app-project/stores/User/UserPersonalization/YourStats/YourStats.spec.js b/packages/app-project/stores/User/UserPersonalization/YourStats/YourStats.spec.js index c2fef22594..7cd621454b 100644 --- a/packages/app-project/stores/User/UserPersonalization/YourStats/YourStats.spec.js +++ b/packages/app-project/stores/User/UserPersonalization/YourStats/YourStats.spec.js @@ -29,6 +29,7 @@ describe('Stores > YourStats', function () { { count: 8, period: '2019-10-05' }, { count: 15, period: '2019-10-06' } ] + nockScope = nock('https://panoptes-staging.zooniverse.org/api') .persist() .get('/project_preferences') @@ -45,13 +46,15 @@ describe('Stores > YourStats', function () { .query(true) .reply(200) rootStore = initStore(true, { project }) - sinon.stub(statsClient, 'request').callsFake(() => Promise.resolve({ statsCount: MOCK_DAILY_COUNTS })) + sinon.stub(statsClient, 'fetchDailyStats').callsFake(({ projectId, userId }) => (projectId === '2' && userId === '123') ? + Promise.resolve({ data: MOCK_DAILY_COUNTS }) : + Promise.reject(new Error(`Unable to fetch stats for project ${projectId} and user ${userId}`))) sinon.stub(talkAPI, 'get') }) after(function () { console.error.restore() - statsClient.request.restore() + statsClient.fetchDailyStats.restore() talkAPI.get.restore() nock.cleanAll() }) @@ -78,25 +81,12 @@ describe('Stores > YourStats', function () { }) it('should request user statistics', function () { - const query = `{ - statsCount( - eventType: "classification", - interval: "1 Day", - window: "1 Week", - projectId: "2", - userId: "123" - ){ - period, - count - } - }` - - expect(statsClient.request).to.have.been.calledOnceWith(query.replace(/\s+/g, ' ')) + expect(statsClient.fetchDailyStats).to.have.been.calledOnceWith({ projectId: '2', userId: '123' }) }) describe('weekly classification stats', function () { it('should be created', function () { - expect(rootStore.user.personalization.stats.thisWeek.length).to.equal(7) + expect(getSnapshot(rootStore.user.personalization.stats.thisWeek).length).to.equal(7) }) it('should start on Monday', function () { diff --git a/packages/app-project/test/create-global-document.js b/packages/app-project/test/create-global-document.js index bcc0a0810f..b9fa68ecda 100644 --- a/packages/app-project/test/create-global-document.js +++ b/packages/app-project/test/create-global-document.js @@ -1,6 +1,7 @@ // Creates a global document object using jsdom to allow the use of the // `mount` method in enzyme. import { JSDOM } from 'jsdom' +// importing fetch from node-fetch and attaching it to the global object so that fetch can be tested with nock. import fetch from 'node-fetch' import nock from 'nock'