{"action":"created","comment":{"url":"https://api.github.com/repos/ParsePlatform/parse-server/pulls/comments/61145582","id":61145582,"diff_hunk":"@@ -0,0 +1,118 @@\n+'use strict'\n+\n+let equal = require('deep-equal');\n+let deepcopy = require('deepcopy');\n+\n+import { logger, configureLogger } from './logger';\n+import cache from './cache';\n+let authDataManager = require('./authDataManager');\n+let database = require('./DatabaseAdapter');\n+\n+const settingsCollectionName = '_ServerSettings';\n+\n+// doesn't make sense to expose / modify these settings\n+const lockedSettings = [\n+  'applicationId',\n+  'masterKey',\n+  'serverURL',\n+  'collectionPrefix',\n+  'filesController',\n+  'pushController',\n+  'loggerController',\n+  'hooksController',\n+  'userController',\n+  'authDataManager',\n+  'liveQueryController',\n+  'enableConfigChanges',\n+  'settingsInitialized'\n+];\n+\n+// settings defined in server configuration\n+let definedSettings = {};\n+\n+// callbacks for specific setting changes\n+let authChange = settings => settings.authDataManager = authDataManager(settings.ouath, settings.enableAnonymousUsers);\n+let onChange = {\n+  logLevel: settings => configureLogger({ level: settings.logLevel }),\n+  verbose: settings => {\n+    settings.logLevel = 'silly';\n+    configureLogger({ level: settings.logLevel });\n+  },\n+  oauth: authChange,\n+  enableAnonymousUsers: authChange,\n+  sessionLength: settings => settings.sessionLength = Number(settings.sessionLength),\n+}\n+\n+export default function SettingsManager(appId) {\n+  return {\n+    pull: _ => {\n+      return getSettingsCollection(appId)\n+        .then(coll => coll.find({ applicationId: appId }, { limit: 1 }))\n+        .then(results => {\n+          let settings = results && results.length && results[0];\n+          if (settings) delete settings._id;\n+          logger.verbose('Pulled settings: ' + JSON.stringify(settings, null, 2));\n+          return settings;\n+        });\n+    },\n+\n+    push: settings => {\n+      settings = deepcopy(settings);\n+      settings.applicationId = appId;\n+      return getSettingsCollection(appId)\n+        .then(coll => coll.upsertOne({applicationId: appId}, { $set: settings }))\n+        .then(_ => {\n+          logger.verbose('Pushed settings: ' + JSON.stringify(settings, null, 2));\n+        });\n+    },\n+\n+    updateCache: (updates = {}) => {\n+      updates = Object.keys(updates)\n+        .filter(update => !equal(updates[update], cache.apps.get(appId)[update]) && !isLocked(update) && !isDefined(update))\n+        .reduce((filtered, update) => {\n+          filtered[update] = updates[update];\n+          return filtered;\n+        }, {});\n+\n+      Object.keys(updates).forEach(setting => {\n+        var config = cache.apps.get(appId);\n+        logger.info(`Setting '${setting}' updated from '${config[setting]}' to '${updates[setting]}'`);\n+        config[setting] = updates[setting];\n+        if (onChange[setting]) onChange[setting](cache.apps.get(appId))\n+      });\n+      return updates;\n+    },\n+\n+    getUnlocked: _ => {\n+      let settings = cache.apps.get(appId);\n+\n+      let settingsString = JSON.stringify(settings, (k, v) => {\n+        if (!lockedSettings.includes(k)) {\n+          if (v === undefined) return null;\n+          return v;\n+        }\n+      });\n+\n+      return JSON.parse(settingsString);\n+    },\n+\n+    setDefined: settings => definedSettings = settings\n+  };\n+\n+  function getSettingsCollection() {\n+    let config = cache.apps.get(appId);\n+    return database.getDatabaseConnection(appId, config.collectionPrefix).adaptiveCollection(settingsCollectionName);","path":"src/SettingsManager.js","position":104,"original_position":104,"commit_id":"83151b7385ac0c141c1b1fc9c8903b79f01253ce","original_commit_id":"83151b7385ac0c141c1b1fc9c8903b79f01253ce","user":{"login":"drew-gross","id":558242,"avatar_url":"https://avatars.githubusercontent.com/u/558242?v=3","gravatar_id":"","url":"https://api.github.com/users/drew-gross","html_url":"https://github.com/drew-gross","followers_url":"https://api.github.com/users/drew-gross/followers","following_url":"https://api.github.com/users/drew-gross/following{/other_user}","gists_url":"https://api.github.com/users/drew-gross/gists{/gist_id}","starred_url":"https://api.github.com/users/drew-gross/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/drew-gross/subscriptions","organizations_url":"https://api.github.com/users/drew-gross/orgs","repos_url":"https://api.github.com/users/drew-gross/repos","events_url":"https://api.github.com/users/drew-gross/events{/privacy}","received_events_url":"https://api.github.com/users/drew-gross/received_events","type":"User","site_admin":false},"body":"We don't want to introduce new uses of `collectionPrefix` inside Parse Server. You should go through the database adapter to retrieve the collection, that will apply the prefix automatically, and also let you avoid referencing the appId in here, I think.","created_at":"2016-04-26T19:07:02Z","updated_at":"2016-04-26T19:07:02Z","html_url":"https://github.com/ParsePlatform/parse-server/pull/1645#discussion_r61145582","pull_request_url":"https://api.github.com/repos/ParsePlatform/parse-server/pulls/1645","_links":{"self":{"href":"https://api.github.com/repos/ParsePlatform/parse-server/pulls/comments/61145582"},"html":{"href":"https://github.com/ParsePlatform/parse-server/pull/1645#discussion_r61145582"},"pull_request":{"href":"https://api.github.com/repos/ParsePlatform/parse-server/pulls/1645"}}},"pull_request":{"url":"https://api.github.com/repos/ParsePlatform/parse-server/pulls/1645","id":67938838,"html_url":"https://github.com/ParsePlatform/parse-server/pull/1645","diff_url":"https://github.com/ParsePlatform/parse-server/pull/1645.diff","patch_url":"https://github.com/ParsePlatform/parse-server/pull/1645.patch","issue_url":"https://api.github.com/repos/ParsePlatform/parse-server/issues/1645","number":1645,"state":"open","locked":false,"title":"Added settings manager","user":{"login":"mamaso","id":9870694,"avatar_url":"https://avatars.githubusercontent.com/u/9870694?v=3","gravatar_id":"","url":"https://api.github.com/users/mamaso","html_url":"https://github.com/mamaso","followers_url":"https://api.github.com/users/mamaso/followers","following_url":"https://api.github.com/users/mamaso/following{/other_user}","gists_url":"https://api.github.com/users/mamaso/gists{/gist_id}","starred_url":"https://api.github.com/users/mamaso/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mamaso/subscriptions","organizations_url":"https://api.github.com/users/mamaso/orgs","repos_url":"https://api.github.com/users/mamaso/repos","events_url":"https://api.github.com/users/mamaso/events{/privacy}","received_events_url":"https://api.github.com/users/mamaso/received_events","type":"User","site_admin":false},"body":"Addresses some of the comments in #1559 \r\n\r\nHighlighting relevant pieces:\r\n\r\n#### Two relevant configuration settings:\r\n- enableConfigChanges: on/off for the whole dynamic settings piece, default off\r\n- lockDefinedSettings: on/off for locking config specified settings, default on.  \r\n\r\n#### Settings Manager\r\nsettings / cache / db management\r\n\r\n#### Parse Server Constructor:\r\n1.  db settings pulled\r\n2.  cached settings updated if necessary\r\n3.  cached settings pushed to db\r\n4.  ParseServer.config.settingsInitialized promise to track state (code smell, but needs a more significant api change otherwise)\r\n\r\n#### SettingsRouter:\r\n- All ops require master key\r\n- POST\r\n  - return error if enableConfigChanges is not true\r\n  - updates values & persists IFF current & update are different\r\n  - returns object with only values which were updated\r\n- GET\r\n  - returns all unlocked settings\r\n  - if a setting is undefined in server configuration, it will appear as null\r\n\r\n#### middlewares.handleParseHeaders:\r\n- fetches database configuration & updates cache once per request IFF enableConfigChanges true\r\n\r\n","created_at":"2016-04-26T18:53:50Z","updated_at":"2016-04-26T19:07:02Z","closed_at":null,"merged_at":null,"merge_commit_sha":"d687809833aab341e885fc545b3711180417c60d","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/ParsePlatform/parse-server/pulls/1645/commits","review_comments_url":"https://api.github.com/repos/ParsePlatform/parse-server/pulls/1645/comments","review_comment_url":"https://api.github.com/repos/ParsePlatform/parse-server/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ParsePlatform/parse-server/issues/1645/comments","statuses_url":"https://api.github.com/repos/ParsePlatform/parse-server/statuses/83151b7385ac0c141c1b1fc9c8903b79f01253ce","head":{"label":"mamaso:settings-endpoint-no-cache","ref":"settings-endpoint-no-cache","sha":"83151b7385ac0c141c1b1fc9c8903b79f01253ce","user":{"login":"mamaso","id":9870694,"avatar_url":"https://avatars.githubusercontent.com/u/9870694?v=3","gravatar_id":"","url":"https://api.github.com/users/mamaso","html_url":"https://github.com/mamaso","followers_url":"https://api.github.com/users/mamaso/followers","following_url":"https://api.github.com/users/mamaso/following{/other_user}","gists_url":"https://api.github.com/users/mamaso/gists{/gist_id}","starred_url":"https://api.github.com/users/mamaso/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mamaso/subscriptions","organizations_url":"https://api.github.com/users/mamaso/orgs","repos_url":"https://api.github.com/users/mamaso/repos","events_url":"https://api.github.com/users/mamaso/events{/privacy}","received_events_url":"https://api.github.com/users/mamaso/received_events","type":"User","site_admin":false},"repo":{"id":51946557,"name":"parse-server","full_name":"mamaso/parse-server","owner":{"login":"mamaso","id":9870694,"avatar_url":"https://avatars.githubusercontent.com/u/9870694?v=3","gravatar_id":"","url":"https://api.github.com/users/mamaso","html_url":"https://github.com/mamaso","followers_url":"https://api.github.com/users/mamaso/followers","following_url":"https://api.github.com/users/mamaso/following{/other_user}","gists_url":"https://api.github.com/users/mamaso/gists{/gist_id}","starred_url":"https://api.github.com/users/mamaso/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mamaso/subscriptions","organizations_url":"https://api.github.com/users/mamaso/orgs","repos_url":"https://api.github.com/users/mamaso/repos","events_url":"https://api.github.com/users/mamaso/events{/privacy}","received_events_url":"https://api.github.com/users/mamaso/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/mamaso/parse-server","description":"Parse-compatible API server module for Node/Express","fork":true,"url":"https://api.github.com/repos/mamaso/parse-server","forks_url":"https://api.github.com/repos/mamaso/parse-server/forks","keys_url":"https://api.github.com/repos/mamaso/parse-server/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mamaso/parse-server/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mamaso/parse-server/teams","hooks_url":"https://api.github.com/repos/mamaso/parse-server/hooks","issue_events_url":"https://api.github.com/repos/mamaso/parse-server/issues/events{/number}","events_url":"https://api.github.com/repos/mamaso/parse-server/events","assignees_url":"https://api.github.com/repos/mamaso/parse-server/assignees{/user}","branches_url":"https://api.github.com/repos/mamaso/parse-server/branches{/branch}","tags_url":"https://api.github.com/repos/mamaso/parse-server/tags","blobs_url":"https://api.github.com/repos/mamaso/parse-server/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mamaso/parse-server/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mamaso/parse-server/git/refs{/sha}","trees_url":"https://api.github.com/repos/mamaso/parse-server/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mamaso/parse-server/statuses/{sha}","languages_url":"https://api.github.com/repos/mamaso/parse-server/languages","stargazers_url":"https://api.github.com/repos/mamaso/parse-server/stargazers","contributors_url":"https://api.github.com/repos/mamaso/parse-server/contributors","subscribers_url":"https://api.github.com/repos/mamaso/parse-server/subscribers","subscription_url":"https://api.github.com/repos/mamaso/parse-server/subscription","commits_url":"https://api.github.com/repos/mamaso/parse-server/commits{/sha}","git_commits_url":"https://api.github.com/repos/mamaso/parse-server/git/commits{/sha}","comments_url":"https://api.github.com/repos/mamaso/parse-server/comments{/number}","issue_comment_url":"https://api.github.com/repos/mamaso/parse-server/issues/comments{/number}","contents_url":"https://api.github.com/repos/mamaso/parse-server/contents/{+path}","compare_url":"https://api.github.com/repos/mamaso/parse-server/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mamaso/parse-server/merges","archive_url":"https://api.github.com/repos/mamaso/parse-server/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mamaso/parse-server/downloads","issues_url":"https://api.github.com/repos/mamaso/parse-server/issues{/number}","pulls_url":"https://api.github.com/repos/mamaso/parse-server/pulls{/number}","milestones_url":"https://api.github.com/repos/mamaso/parse-server/milestones{/number}","notifications_url":"https://api.github.com/repos/mamaso/parse-server/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mamaso/parse-server/labels{/name}","releases_url":"https://api.github.com/repos/mamaso/parse-server/releases{/id}","deployments_url":"https://api.github.com/repos/mamaso/parse-server/deployments","created_at":"2016-02-17T18:48:48Z","updated_at":"2016-02-17T18:48:49Z","pushed_at":"2016-04-26T18:34:54Z","git_url":"git://github.com/mamaso/parse-server.git","ssh_url":"git@github.com:mamaso/parse-server.git","clone_url":"https://github.com/mamaso/parse-server.git","svn_url":"https://github.com/mamaso/parse-server","homepage":null,"size":1542,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"ParsePlatform:settings-endpoint","ref":"settings-endpoint","sha":"4462145c6f52532dc1bd291b5ea40d0441ccfb86","user":{"login":"ParsePlatform","id":1294580,"avatar_url":"https://avatars.githubusercontent.com/u/1294580?v=3","gravatar_id":"","url":"https://api.github.com/users/ParsePlatform","html_url":"https://github.com/ParsePlatform","followers_url":"https://api.github.com/users/ParsePlatform/followers","following_url":"https://api.github.com/users/ParsePlatform/following{/other_user}","gists_url":"https://api.github.com/users/ParsePlatform/gists{/gist_id}","starred_url":"https://api.github.com/users/ParsePlatform/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ParsePlatform/subscriptions","organizations_url":"https://api.github.com/users/ParsePlatform/orgs","repos_url":"https://api.github.com/users/ParsePlatform/repos","events_url":"https://api.github.com/users/ParsePlatform/events{/privacy}","received_events_url":"https://api.github.com/users/ParsePlatform/received_events","type":"Organization","site_admin":false},"repo":{"id":50603846,"name":"parse-server","full_name":"ParsePlatform/parse-server","owner":{"login":"ParsePlatform","id":1294580,"avatar_url":"https://avatars.githubusercontent.com/u/1294580?v=3","gravatar_id":"","url":"https://api.github.com/users/ParsePlatform","html_url":"https://github.com/ParsePlatform","followers_url":"https://api.github.com/users/ParsePlatform/followers","following_url":"https://api.github.com/users/ParsePlatform/following{/other_user}","gists_url":"https://api.github.com/users/ParsePlatform/gists{/gist_id}","starred_url":"https://api.github.com/users/ParsePlatform/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ParsePlatform/subscriptions","organizations_url":"https://api.github.com/users/ParsePlatform/orgs","repos_url":"https://api.github.com/users/ParsePlatform/repos","events_url":"https://api.github.com/users/ParsePlatform/events{/privacy}","received_events_url":"https://api.github.com/users/ParsePlatform/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ParsePlatform/parse-server","description":"Parse-compatible API server module for Node/Express","fork":false,"url":"https://api.github.com/repos/ParsePlatform/parse-server","forks_url":"https://api.github.com/repos/ParsePlatform/parse-server/forks","keys_url":"https://api.github.com/repos/ParsePlatform/parse-server/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ParsePlatform/parse-server/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ParsePlatform/parse-server/teams","hooks_url":"https://api.github.com/repos/ParsePlatform/parse-server/hooks","issue_events_url":"https://api.github.com/repos/ParsePlatform/parse-server/issues/events{/number}","events_url":"https://api.github.com/repos/ParsePlatform/parse-server/events","assignees_url":"https://api.github.com/repos/ParsePlatform/parse-server/assignees{/user}","branches_url":"https://api.github.com/repos/ParsePlatform/parse-server/branches{/branch}","tags_url":"https://api.github.com/repos/ParsePlatform/parse-server/tags","blobs_url":"https://api.github.com/repos/ParsePlatform/parse-server/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ParsePlatform/parse-server/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ParsePlatform/parse-server/git/refs{/sha}","trees_url":"https://api.github.com/repos/ParsePlatform/parse-server/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ParsePlatform/parse-server/statuses/{sha}","languages_url":"https://api.github.com/repos/ParsePlatform/parse-server/languages","stargazers_url":"https://api.github.com/repos/ParsePlatform/parse-server/stargazers","contributors_url":"https://api.github.com/repos/ParsePlatform/parse-server/contributors","subscribers_url":"https://api.github.com/repos/ParsePlatform/parse-server/subscribers","subscription_url":"https://api.github.com/repos/ParsePlatform/parse-server/subscription","commits_url":"https://api.github.com/repos/ParsePlatform/parse-server/commits{/sha}","git_commits_url":"https://api.github.com/repos/ParsePlatform/parse-server/git/commits{/sha}","comments_url":"https://api.github.com/repos/ParsePlatform/parse-server/comments{/number}","issue_comment_url":"https://api.github.com/repos/ParsePlatform/parse-server/issues/comments{/number}","contents_url":"https://api.github.com/repos/ParsePlatform/parse-server/contents/{+path}","compare_url":"https://api.github.com/repos/ParsePlatform/parse-server/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ParsePlatform/parse-server/merges","archive_url":"https://api.github.com/repos/ParsePlatform/parse-server/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ParsePlatform/parse-server/downloads","issues_url":"https://api.github.com/repos/ParsePlatform/parse-server/issues{/number}","pulls_url":"https://api.github.com/repos/ParsePlatform/parse-server/pulls{/number}","milestones_url":"https://api.github.com/repos/ParsePlatform/parse-server/milestones{/number}","notifications_url":"https://api.github.com/repos/ParsePlatform/parse-server/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ParsePlatform/parse-server/labels{/name}","releases_url":"https://api.github.com/repos/ParsePlatform/parse-server/releases{/id}","deployments_url":"https://api.github.com/repos/ParsePlatform/parse-server/deployments","created_at":"2016-01-28T18:29:14Z","updated_at":"2016-04-26T17:41:03Z","pushed_at":"2016-04-26T18:53:51Z","git_url":"git://github.com/ParsePlatform/parse-server.git","ssh_url":"git@github.com:ParsePlatform/parse-server.git","clone_url":"https://github.com/ParsePlatform/parse-server.git","svn_url":"https://github.com/ParsePlatform/parse-server","homepage":"https://parse.com","size":3304,"stargazers_count":8688,"watchers_count":8688,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2110,"mirror_url":null,"open_issues_count":81,"forks":2110,"open_issues":81,"watchers":8688,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/ParsePlatform/parse-server/pulls/1645"},"html":{"href":"https://github.com/ParsePlatform/parse-server/pull/1645"},"issue":{"href":"https://api.github.com/repos/ParsePlatform/parse-server/issues/1645"},"comments":{"href":"https://api.github.com/repos/ParsePlatform/parse-server/issues/1645/comments"},"review_comments":{"href":"https://api.github.com/repos/ParsePlatform/parse-server/pulls/1645/comments"},"review_comment":{"href":"https://api.github.com/repos/ParsePlatform/parse-server/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ParsePlatform/parse-server/pulls/1645/commits"},"statuses":{"href":"https://api.github.com/repos/ParsePlatform/parse-server/statuses/83151b7385ac0c141c1b1fc9c8903b79f01253ce"}}}}