From 974fcb973dad6cb31819c64a59402e932afbc9b1 Mon Sep 17 00:00:00 2001 From: Umut Benzer Date: Fri, 24 May 2024 14:53:14 +0200 Subject: [PATCH 01/13] IDS-4707 - Switch from InputCombo to InputText if there are too many connections to list --- client/utils/useDefaultFields.js | 9 ++++++--- package.json | 6 ++---- server/lib/multipartRequest.js | 10 +++++++++- server/routes/connections.js | 4 +++- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/client/utils/useDefaultFields.js b/client/utils/useDefaultFields.js index 4d05bab60..7ca5fe10d 100644 --- a/client/utils/useDefaultFields.js +++ b/client/utils/useDefaultFields.js @@ -1,5 +1,7 @@ import _ from 'lodash'; +const CONNECTIONS_LIST_LIMIT = 20; + const applyDefaults = (type, fields, property, defaults) => { const field = _.find(fields, { property }); @@ -61,14 +63,15 @@ export const useConnectionsField = (isEditField, fields, connections, onConnecti return _.remove(fields, { property: 'connection' }); } + const isConnectionListingLimitExceeded = connections.length >= CONNECTIONS_LIST_LIMIT; const defaults = { property: 'connection', label: 'Connection', [type]: { required: true, - type: 'select', - component: 'InputCombo', - options: connections.map(conn => ({ value: conn.name, label: conn.name })), + type: 'text', + component: isConnectionListingLimitExceeded ? 'InputText' :'InputCombo', + options: isConnectionListingLimitExceeded ? undefined : connections.map(conn => ({ value: conn.name, label: conn.name })), onChange: onConnectionChange } }; diff --git a/package.json b/package.json index e2dce3e55..7f9e00b68 100644 --- a/package.json +++ b/package.json @@ -14,15 +14,13 @@ "deploy": "a0-ext deploy --package ./dist/package.zip --url http://0.0.0.0:3000/api/extensions", "client:build": "a0-ext build:client ./client/app.jsx ./dist/client", "extension:build": "a0-ext build:server ./webtask.js ./dist && cp ./dist/auth0-delegated-admin.extension.$npm_package_version.js ./build/bundle.js && cp ./webtask.json ./dist/webtask.json", - "serve:dev": "cross-env NODE_ENV=development nodemon -e js --ignore assets/app/ --ignore build/webpack/ --ignore client/ --ignore server/data.json --ignore node_modules/ ./build/webpack/server.js", + "serve:dev": "cross-env NODE_ENV=development NODE_OPTIONS=--openssl-legacy-provider nodemon -e js --ignore assets/app/ --ignore build/webpack/ --ignore client/ --ignore server/data.json --ignore node_modules/ ./build/webpack/server.js", "serve:prod": "cross-env NODE_ENV=production node index.js", "test": "cross-env NODE_ENV=test nyc --reporter=lcov mocha --require ignore-styles tests/mocha.js './tests/**/*.tests.js'", "test:watch": "cross-env NODE_ENV=test mocha --require ignore-styles tests/mocha.js './tests/**/*.tests.js' --watch", "test:pre": "npm run test:clean && npm run lint:js", "test:clean": "rimraf ./coverage && rimraf ./.nyc_output", - "extension:size": "cross-env NODE_ENV=production webpack -p --config ./build/extension/webpack.config.js --json > ./build/extension/bundle-size.json && node ./build/extension/bundle-size.js", - "snyk-protect": "snyk protect", - "prepare": "npm run snyk-protect" + "extension:size": "cross-env NODE_ENV=production webpack -p --config ./build/extension/webpack.config.js --json > ./build/extension/bundle-size.json && node ./build/extension/bundle-size.js" }, "keywords": [ "auth0", diff --git a/server/lib/multipartRequest.js b/server/lib/multipartRequest.js index 007830566..ad29f2975 100644 --- a/server/lib/multipartRequest.js +++ b/server/lib/multipartRequest.js @@ -1,7 +1,12 @@ import Promise from 'bluebird'; import { ArgumentError } from 'auth0-extension-tools'; -export default function(client, entity, opts = {}, perPage = 50, concurrency = 5) { + +export default function(client, entity, opts = {}, fetchOptions = {} ) { + const perPage = fetchOptions.perPage || 50; + const concurrency = fetchOptions.concurrency || 5; + const limit = fetchOptions.limit || null; + if (client === null || client === undefined) { throw new ArgumentError('Must provide a auth0 client object.'); } @@ -20,6 +25,9 @@ export default function(client, entity, opts = {}, perPage = 50, concurrency = 5 getter({ ...options, include_totals: true, page: 0 }) .then((response) => { total = response.total || 0; + if (limit) { + total = Math.min(total, limit); + } pageCount = Math.ceil(total / perPage); const data = response[entity] || response || []; data.forEach(item => result.push(item)); diff --git a/server/routes/connections.js b/server/routes/connections.js index c12e2dbe4..74e728779 100644 --- a/server/routes/connections.js +++ b/server/routes/connections.js @@ -3,10 +3,12 @@ import { Router } from 'express'; import multipartRequest from '../lib/multipartRequest'; +const CONNECTIONS_LIST_LIMIT = 50; + export default (scriptManager) => { const api = Router(); api.get('/', (req, res, next) => { - multipartRequest(req.auth0, 'connections', { strategy: 'auth0', fields: 'id,name,strategy,options' }) + multipartRequest(req.auth0, 'connections', { strategy: 'auth0', fields: 'id,name,strategy,options' }, { perPage: 100, limit: CONNECTIONS_LIST_LIMIT}) .then((connections) => { global.connections = connections.map(conn => ({ name: conn.name, id: conn.id })); const settingsContext = { From b3982949c4bb13f657d5f17abc6f5315fdec4717 Mon Sep 17 00:00:00 2001 From: Tim Saunders Date: Tue, 28 May 2024 19:24:49 +0100 Subject: [PATCH 02/13] Fix test + QoL improvements --- client/actions/user.js | 2 +- client/constants.js | 2 ++ client/utils/useDefaultFields.js | 10 +++++----- server/routes/connections.js | 3 +-- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/client/actions/user.js b/client/actions/user.js index 558b44273..0f8041572 100644 --- a/client/actions/user.js +++ b/client/actions/user.js @@ -80,7 +80,7 @@ export function requestCreateUser(memberships) { dispatch({ type: constants.REQUEST_CREATE_USER, payload: { - connection: connections && connections.length && connections[0].name, + connection: connections && connections.length && connections.length < 20 && connections[0].name || null, memberships: memberships && memberships.length === 1 ? [ memberships[0] ] : [ ] } }); diff --git a/client/constants.js b/client/constants.js index dfdc92925..90737185d 100644 --- a/client/constants.js +++ b/client/constants.js @@ -253,3 +253,5 @@ export const SUPER_ADMIN = 2; // The list of reserved user fields that must not be rendered in the custom fields edit form export const RESERVED_USER_FIELDS = [ 'username', 'memberships', 'connection', 'password', 'email', 'repeatPassword', 'resetPassword' ]; + +export const CONNECTIONS_LIST_LIMIT = 20; \ No newline at end of file diff --git a/client/utils/useDefaultFields.js b/client/utils/useDefaultFields.js index 7ca5fe10d..cf1055580 100644 --- a/client/utils/useDefaultFields.js +++ b/client/utils/useDefaultFields.js @@ -1,6 +1,6 @@ import _ from 'lodash'; -const CONNECTIONS_LIST_LIMIT = 20; +import { CONNECTIONS_LIST_LIMIT } from "../constants"; const applyDefaults = (type, fields, property, defaults) => { const field = _.find(fields, { property }); @@ -63,14 +63,14 @@ export const useConnectionsField = (isEditField, fields, connections, onConnecti return _.remove(fields, { property: 'connection' }); } - const isConnectionListingLimitExceeded = connections.length >= CONNECTIONS_LIST_LIMIT; + const isConnectionListingLimitExceeded = connections.length >= CONNECTIONS_LIST_LIMIT; const defaults = { property: 'connection', - label: 'Connection', + label: isConnectionListingLimitExceeded ? 'Connection Name' : 'Connection', [type]: { required: true, - type: 'text', - component: isConnectionListingLimitExceeded ? 'InputText' :'InputCombo', + type: isConnectionListingLimitExceeded ? 'text' : 'select', + component: isConnectionListingLimitExceeded ? 'InputText' : 'InputCombo', options: isConnectionListingLimitExceeded ? undefined : connections.map(conn => ({ value: conn.name, label: conn.name })), onChange: onConnectionChange } diff --git a/server/routes/connections.js b/server/routes/connections.js index 74e728779..ece19e8ea 100644 --- a/server/routes/connections.js +++ b/server/routes/connections.js @@ -2,8 +2,7 @@ import _ from 'lodash'; import { Router } from 'express'; import multipartRequest from '../lib/multipartRequest'; - -const CONNECTIONS_LIST_LIMIT = 50; +import { CONNECTIONS_LIST_LIMIT } from "../constants"; export default (scriptManager) => { const api = Router(); From 340edf0170d77539afd0eaea7af2a5ded4e02635 Mon Sep 17 00:00:00 2001 From: Tim Saunders Date: Wed, 29 May 2024 17:59:42 +0100 Subject: [PATCH 03/13] Fix connections fetch limit --- client/actions/user.js | 4 ++-- server/routes/connections.js | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/client/actions/user.js b/client/actions/user.js index 0f8041572..6aba6778c 100644 --- a/client/actions/user.js +++ b/client/actions/user.js @@ -5,7 +5,7 @@ import * as constants from '../constants'; import { fetchUserLogs } from './userLog'; import { fetchUserDevices } from './userDevice'; import { getAccessLevel } from './auth'; -import { removeBlockedIPs } from "../reducers/removeBlockedIPs"; +import { CONNECTIONS_LIST_LIMIT } from "../constants"; const addRequiredTextParam = (url, languageDictionary) => { languageDictionary = languageDictionary || {}; @@ -80,7 +80,7 @@ export function requestCreateUser(memberships) { dispatch({ type: constants.REQUEST_CREATE_USER, payload: { - connection: connections && connections.length && connections.length < 20 && connections[0].name || null, + connection: connections && connections.length && connections.length < CONNECTIONS_LIST_LIMIT && connections[0].name || null, memberships: memberships && memberships.length === 1 ? [ memberships[0] ] : [ ] } }); diff --git a/server/routes/connections.js b/server/routes/connections.js index ece19e8ea..424c08676 100644 --- a/server/routes/connections.js +++ b/server/routes/connections.js @@ -2,12 +2,13 @@ import _ from 'lodash'; import { Router } from 'express'; import multipartRequest from '../lib/multipartRequest'; -import { CONNECTIONS_LIST_LIMIT } from "../constants"; +// only fetch one page of connections +const CONNECTIONS_FETCH_LIMIT = 50; export default (scriptManager) => { const api = Router(); api.get('/', (req, res, next) => { - multipartRequest(req.auth0, 'connections', { strategy: 'auth0', fields: 'id,name,strategy,options' }, { perPage: 100, limit: CONNECTIONS_LIST_LIMIT}) + multipartRequest(req.auth0, 'connections', { strategy: 'auth0', fields: 'id,name,strategy,options' }, { perPage: 100, limit: CONNECTIONS_FETCH_LIMIT}) .then((connections) => { global.connections = connections.map(conn => ({ name: conn.name, id: conn.id })); const settingsContext = { From b623d9f3a4f49df44e1af1f57c08748891c435ff Mon Sep 17 00:00:00 2001 From: Tim Saunders Date: Thu, 30 May 2024 09:51:49 +0100 Subject: [PATCH 04/13] Use default perPage for connections --- server/routes/connections.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/routes/connections.js b/server/routes/connections.js index 424c08676..da88f93be 100644 --- a/server/routes/connections.js +++ b/server/routes/connections.js @@ -8,7 +8,7 @@ const CONNECTIONS_FETCH_LIMIT = 50; export default (scriptManager) => { const api = Router(); api.get('/', (req, res, next) => { - multipartRequest(req.auth0, 'connections', { strategy: 'auth0', fields: 'id,name,strategy,options' }, { perPage: 100, limit: CONNECTIONS_FETCH_LIMIT}) + multipartRequest(req.auth0, 'connections', { strategy: 'auth0', fields: 'id,name,strategy,options' }, { limit: CONNECTIONS_FETCH_LIMIT}) .then((connections) => { global.connections = connections.map(conn => ({ name: conn.name, id: conn.id })); const settingsContext = { From eee0a90850fd7a60b4349d4418a886160db8c4a2 Mon Sep 17 00:00:00 2001 From: Tim Saunders Date: Fri, 31 May 2024 17:25:19 +0100 Subject: [PATCH 05/13] set CONNECTIONS_LIST_LIMIT to 20k --- client/constants.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/constants.js b/client/constants.js index 90737185d..8c0ce670a 100644 --- a/client/constants.js +++ b/client/constants.js @@ -254,4 +254,4 @@ export const SUPER_ADMIN = 2; // The list of reserved user fields that must not be rendered in the custom fields edit form export const RESERVED_USER_FIELDS = [ 'username', 'memberships', 'connection', 'password', 'email', 'repeatPassword', 'resetPassword' ]; -export const CONNECTIONS_LIST_LIMIT = 20; \ No newline at end of file +export const CONNECTIONS_LIST_LIMIT = 20000; From 04b29ac9dee5a997be212c25c9b6348dbb601494 Mon Sep 17 00:00:00 2001 From: Tim Saunders Date: Mon, 3 Jun 2024 15:29:36 +0100 Subject: [PATCH 06/13] Don't fetch records if total > limit --- client/actions/user.js | 8 ++++++-- client/constants.js | 2 -- client/containers/Users/Users.jsx | 2 +- client/utils/useDefaultFields.js | 26 ++++++++++++++--------- server/lib/multipartRequest.js | 34 ++++++++++++++++++++++++------- server/routes/connections.js | 26 ++++++++++++++++++----- 6 files changed, 71 insertions(+), 27 deletions(-) diff --git a/client/actions/user.js b/client/actions/user.js index 6aba6778c..9a505098d 100644 --- a/client/actions/user.js +++ b/client/actions/user.js @@ -5,7 +5,6 @@ import * as constants from '../constants'; import { fetchUserLogs } from './userLog'; import { fetchUserDevices } from './userDevice'; import { getAccessLevel } from './auth'; -import { CONNECTIONS_LIST_LIMIT } from "../constants"; const addRequiredTextParam = (url, languageDictionary) => { languageDictionary = languageDictionary || {}; @@ -77,10 +76,15 @@ export function createUser(user, languageDictionary) { export function requestCreateUser(memberships) { return (dispatch, getState) => { const connections = getState().connections.get('records').toJS(); + + const connection = connections.length === 0 + ? null + : connections && connections.length && connections[0].name + dispatch({ type: constants.REQUEST_CREATE_USER, payload: { - connection: connections && connections.length && connections.length < CONNECTIONS_LIST_LIMIT && connections[0].name || null, + connection, memberships: memberships && memberships.length === 1 ? [ memberships[0] ] : [ ] } }); diff --git a/client/constants.js b/client/constants.js index 8c0ce670a..dfdc92925 100644 --- a/client/constants.js +++ b/client/constants.js @@ -253,5 +253,3 @@ export const SUPER_ADMIN = 2; // The list of reserved user fields that must not be rendered in the custom fields edit form export const RESERVED_USER_FIELDS = [ 'username', 'memberships', 'connection', 'password', 'email', 'repeatPassword', 'resetPassword' ]; - -export const CONNECTIONS_LIST_LIMIT = 20000; diff --git a/client/containers/Users/Users.jsx b/client/containers/Users/Users.jsx index 62044f913..e3140ff54 100644 --- a/client/containers/Users/Users.jsx +++ b/client/containers/Users/Users.jsx @@ -102,7 +102,7 @@ class Users extends Component {

{languageDictionary.usersTitle || 'Users'}

- {(connections.length && role > 0 && showCreateUser) ? + {( role > 0 && showCreateUser) ?