Merge pull request #6151 from overleaf/jpa-jel-ta-spelling-client-cache

[misc] filter out saved words from users dict client side

GitOrigin-RevId: 01b496c60d25954c8e65a71c06fd90a6c428a698
This commit is contained in:
Jakob Ackermann 2022-01-04 11:07:10 +00:00 committed by Copybot
parent 847795e10f
commit 17eb841b31
8 changed files with 66 additions and 6 deletions

View file

@ -54,6 +54,14 @@ const LearnedWordsManager = {
metrics.inc('mongoCache', 0.1, { status: 'miss' })
logger.info({ userToken }, 'mongoCache miss')
LearnedWordsManager.getLearnedWordsNoCache(userToken, (err, words) => {
if (err) return callback(err)
mongoCache.set(userToken, words)
callback(null, words)
})
},
getLearnedWordsNoCache(userToken, callback) {
db.spellingPreferences.findOne(
{ token: userToken },
function (error, preferences) {
@ -68,7 +76,6 @@ const LearnedWordsManager = {
(value, index, self) => self.indexOf(value) === index
)
}
mongoCache.set(userToken, words)
callback(null, words)
}
)

View file

@ -51,7 +51,7 @@ const SpellingAPIManager = {
},
getDic(token, callback) {
return LearnedWordsManager.getLearnedWords(token, callback)
return LearnedWordsManager.getLearnedWordsNoCache(token, callback)
},
}
@ -67,7 +67,7 @@ const promises = {
const misspellings = await ASpell.promises.checkWords(lang, wordSlice)
if (token) {
if (token && !request.skipLearnedWords) {
const learnedWords = await LearnedWordsManager.promises.getLearnedWords(
token
)

View file

@ -40,6 +40,7 @@ const Modules = require('../../infrastructure/Modules')
const SplitTestV2Handler = require('../SplitTests/SplitTestV2Handler')
const { getNewLogsUIVariantForUser } = require('../Helpers/NewLogsUI')
const FeaturesUpdater = require('../Subscription/FeaturesUpdater')
const SpellingHandler = require('../Spelling/SpellingHandler')
const _ssoAvailable = (affiliation, session, linkedInstitutionIds) => {
if (!affiliation.institution) return false
@ -691,6 +692,12 @@ const ProjectController = {
)
}
},
learnedWords(cb) {
if (!userId) {
return cb(null, [])
}
SpellingHandler.getUserDictionaryWithRetries(userId, cb)
},
subscription(cb) {
if (userId == null) {
return cb()
@ -776,6 +783,7 @@ const ProjectController = {
{
project,
user,
learnedWords,
subscription,
isTokenMember,
brandVariation,
@ -927,6 +935,7 @@ const ProjectController = {
isTokenMember
),
languages: Settings.languages,
learnedWords,
editorThemes: THEME_LIST,
maxDocLength: Settings.max_doc_length,
useV2History:

View file

@ -1,10 +1,39 @@
const request = require('request')
const requestRetry = require('requestretry')
const Settings = require('@overleaf/settings')
const OError = require('@overleaf/o-error')
const TIMEOUT = 10 * 1000
module.exports = {
getUserDictionaryWithRetries(userId, callback) {
const options = {
url: `${Settings.apis.spelling.url}/user/${userId}`,
timeout: 3 * 1000,
json: true,
retryDelay: 1,
maxAttempts: 3,
}
requestRetry(options, (error, response, body) => {
if (error) {
return callback(
OError.tag(error, 'error getting user dictionary', { error, userId })
)
}
if (response.statusCode !== 200) {
return callback(
new OError(
'Non-success code from spelling API when getting user dictionary',
{ userId, statusCode: response.statusCode }
)
)
}
callback(null, body)
})
},
getUserDictionary(userId, callback) {
const url = `${Settings.apis.spelling.url}/user/${userId}`
request.get({ url: url, timeout: TIMEOUT }, (error, response) => {

View file

@ -123,6 +123,7 @@ block append meta
meta(name="ol-project_id" content=project_id)
meta(name="ol-userSettings" data-type="json" content=userSettings)
meta(name="ol-user" data-type="json" content=user)
meta(name="ol-learnedWords" data-type="json" content=learnedWords)
meta(name="ol-anonymous" data-type="boolean" content=anonymous)
meta(name="ol-brandVariation" data-type="json" content=brandVariation)
meta(name="ol-anonymousAccessToken" content=anonymousAccessToken)

View file

@ -1,3 +1,5 @@
import getMeta from '../../../../../utils/meta'
// eslint-disable-next-line prefer-regex-literals
const BLACKLISTED_COMMAND_REGEX = new RegExp(
`\
@ -58,6 +60,8 @@ class SpellCheckManager {
this.selectedHighlightContents = null
this.learnedWords = new Set(getMeta('ol-learnedWords'))
$(document).on('click', e => {
// There is a bug (?) in Safari when ctrl-clicking an element, and the
// the contextmenu event is preventDefault-ed. In this case, the
@ -191,6 +195,7 @@ class SpellCheckManager {
this.adapter.highlightedWordManager.removeWord(highlight.word)
const language = this.$scope.spellCheckLanguage
this.cache.put(`${language}:${highlight.word}`, true)
this.learnedWords.add(highlight.word)
}
markLinesAsUpdated(change) {
@ -291,7 +296,7 @@ class SpellCheckManager {
} else {
this.inProgressRequest = this.apiRequest(
'/check',
{ language, words },
{ language, words, skipLearnedWords: true },
(error, result) => {
delete this.inProgressRequest
if (error != null || result == null || result.misspellings == null) {
@ -372,8 +377,10 @@ class SpellCheckManager {
if (word[word.length - 1] === "'") {
word = word.slice(0, -1)
}
positions.push({ row: rowIdx, column: result.index })
words.push(word)
if (!this.learnedWords.has(word)) {
positions.push({ row: rowIdx, column: result.index })
words.push(word)
}
}
}
return { words, positions }

View file

@ -223,6 +223,7 @@ export default describe('SpellCheckManager', function () {
.expect('POST', '/spelling/check', {
language: this.scope.spellCheckLanguage,
words: ['Lorem', 'ipsum', 'dolor'],
skipLearnedWords: true,
token: window.user.id,
_csrf: window.csrfToken,
})
@ -244,6 +245,7 @@ export default describe('SpellCheckManager', function () {
.expect('POST', '/spelling/check', {
language: this.scope.spellCheckLanguage,
words: ['Lorem', 'ipsum', 'dolor'],
skipLearnedWords: true,
token: window.user.id,
_csrf: window.csrfToken,
})
@ -267,6 +269,7 @@ export default describe('SpellCheckManager', function () {
.expect('POST', '/spelling/check', {
language: this.scope.spellCheckLanguage,
words: ['Lorem', 'ipsum', 'dolor'],
skipLearnedWords: true,
token: window.user.id,
_csrf: window.csrfToken,
})
@ -287,6 +290,7 @@ export default describe('SpellCheckManager', function () {
.expect('POST', '/spelling/check', {
language: this.scope.spellCheckLanguage,
words: ['sit', 'amet'],
skipLearnedWords: true,
token: window.user.id,
_csrf: window.csrfToken,
})

View file

@ -193,6 +193,9 @@ describe('ProjectController', function () {
hooks: { fire: sinon.stub().yields(null, []) },
},
'../Helpers/NewLogsUI': this.NewLogsUIHelper,
'../Spelling/SpellingHandler': {
getUserDictionaryWithRetries: sinon.stub().yields(null, []),
},
},
})