From 5776e69bfa884f8cd36219160c70c729e43b7e7b Mon Sep 17 00:00:00 2001 From: vardhan0604 Date: Wed, 26 Jun 2024 23:35:01 +0530 Subject: [PATCH 01/17] feat:Add relational filter conditions (#2496) --- Parse-Dashboard/parse-dashboard-config.json | 12 +-- parse-dashboard@6.0.0-alpha.7 | 0 .../BrowserFilter/BrowserFilter.react.js | 46 +++++++--- .../BrowserFilter/BrowserFilter.scss | 11 ++- .../BrowserFilter/FilterRow.react.js | 48 +++++++++- src/components/Filter/Filter.react.js | 90 +++++++++++++++---- .../PushAudienceDialog.react.js | 4 +- src/dashboard/Data/Browser/Browser.react.js | 40 +++++++-- .../Data/Browser/BrowserToolbar.react.js | 43 +++++++-- .../Data/Browser/DataBrowser.react.js | 25 ++++++ .../Data/Browser/ObjectPickerDialog.react.js | 10 +-- src/dashboard/Push/PushAudiencesData.react.js | 4 +- .../Push/PushAudiencesIndex.react.js | 4 +- src/dashboard/Push/PushNew.react.js | 9 +- src/lib/Filters.js | 25 ++++++ src/lib/queryFromFilters.js | 90 ++++++++++++++++--- 16 files changed, 379 insertions(+), 82 deletions(-) create mode 100644 parse-dashboard@6.0.0-alpha.7 diff --git a/Parse-Dashboard/parse-dashboard-config.json b/Parse-Dashboard/parse-dashboard-config.json index 84d5d15396..c0a709f3ac 100644 --- a/Parse-Dashboard/parse-dashboard-config.json +++ b/Parse-Dashboard/parse-dashboard-config.json @@ -2,13 +2,9 @@ "apps": [ { "serverURL": "http://localhost:1337/parse", - "appId": "hello", - "masterKey": "world", - "appName": "", - "iconName": "", - "primaryBackgroundColor": "", - "secondaryBackgroundColor": "" + "appId": "1234", + "masterKey": "12345", + "appName": "MyApp" } - ], - "iconsFolder": "icons" + ] } diff --git a/parse-dashboard@6.0.0-alpha.7 b/parse-dashboard@6.0.0-alpha.7 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/components/BrowserFilter/BrowserFilter.react.js b/src/components/BrowserFilter/BrowserFilter.react.js index 4f04d2561f..d379129ebf 100644 --- a/src/components/BrowserFilter/BrowserFilter.react.js +++ b/src/components/BrowserFilter/BrowserFilter.react.js @@ -46,13 +46,18 @@ export default class BrowserFilter extends React.Component { toggle() { let filters = this.props.filters; if (this.props.filters.size === 0) { - const available = Filters.availableFilters( - this.props.schema, - null, - this.state.blacklistedFilters + const available = Filters.findRelatedClasses( + this.props.className, + this.props.AllclassesSchema, + this.state.blacklistedFilters, + this.state.filters ); - const field = Object.keys(available)[0]; - filters = new List([new Map({ field: field, constraint: available[field][0] })]); + const filterClass = Object.keys(available)[0]; + const filterField = Object.keys(available[filterClass])[0]; + const filterConstraint = available[filterClass][filterField][0]; + filters = new List([ + new Map({ class: filterClass, field: filterField, constraint: filterConstraint }), + ]); } this.setState(prevState => ({ open: !prevState.open, @@ -65,14 +70,19 @@ export default class BrowserFilter extends React.Component { } addRow() { - const available = Filters.availableFilters( - this.props.schema, - this.state.filters, - this.state.blacklistedFilters + const available = Filters.findRelatedClasses( + this.props.className, + this.props.AllclassesSchema, + this.state.blacklistedFilters, + this.state.filters ); - const field = Object.keys(available)[0]; + const filterClass = Object.keys(available)[0]; + const filterField = Object.keys(available[filterClass])[0]; + const filterConstraint = available[filterClass][filterField][0]; this.setState(({ filters }) => ({ - filters: filters.push(new Map({ field: field, constraint: available[field][0] })), + filters: filters.push( + new Map({ class: filterClass, field: filterField, constraint: filterConstraint }) + ), editMode: true, })); } @@ -125,7 +135,12 @@ export default class BrowserFilter extends React.Component { if (this.props.filters.size) { popoverStyle.push(styles.active); } - const available = Filters.availableFilters(this.props.schema, this.state.filters); + const available = Filters.findRelatedClasses( + this.props.className, + this.props.AllclassesSchema, + this.state.blacklistedFilters, + this.state.filters + ); popover = ( this.setState({ filters: filters })} onSearch={this.apply.bind(this)} + Allclasses={this.props.AllclassesSchema} + AllclassesSchema={Filters.findRelatedClasses( + this.props.className, + this.props.AllclassesSchema + )} renderRow={props => ( { + const buildFieldSuggestions = input => { const regex = new RegExp(input.split('').join('.*?'), 'i'); return fields.filter(f => regex.test(f)); }; + const buildClassSuggestions = input => { + const regex = new RegExp(input.split('').join('.*?'), 'i'); + return classes.filter(f => regex.test(f)); + }; return ( -
+
+ ''} + /> ''} /> { +const Filter = ({ + schema, + filters, + Allclasses, + AllclassesSchema, + renderRow, + onChange, + onSearch, + blacklist, + className, +}) => { const currentApp = React.useContext(CurrentApp); blacklist = blacklist || []; - const available = Filters.availableFilters(schema, filters); + const available = Filters.findRelatedClasses(className, Allclasses, blacklist, filters); + const classes = Object.keys(available).concat([]); return ( -
+
{filters.toArray().map((filter, i) => { + const currentClassName = filter.get('class'); const field = filter.get('field'); const constraint = filter.get('constraint'); const compareTo = filter.get('compareTo'); - - const fields = Object.keys(available).concat([]); + let fields = []; + if (available[currentClassName]) { + fields = Object.keys(available[currentClassName]).concat([]); + } if (fields.indexOf(field) < 0) { fields.push(field); } @@ -97,31 +146,36 @@ const Filter = ({ schema, filters, renderRow, onChange, onSearch, blacklist, cla else { fields.sort(); } - - const constraints = Filters.FieldConstraints[schema[field].type].filter( + const constraints = Filters.FieldConstraints[schema[currentClassName][field].type].filter( c => blacklist.indexOf(c) < 0 ); - let compareType = schema[field].type; + let compareType = schema[currentClassName][field].type; if (Object.prototype.hasOwnProperty.call(Filters.Constraints[constraint], 'field')) { compareType = Filters.Constraints[constraint].field; } return renderRow({ + classes, fields, constraints, compareInfo: { type: compareType, - targetClass: schema[field].targetClass, + targetClass: schema[currentClassName][field].targetClass, }, + currentClass: currentClassName, currentField: field, currentConstraint: constraint, compareTo, key: field + '-' + constraint + '-' + i, - + onChangeClass: newClassName => { + onChange(changeClass(schema, filters, i, newClassName)); + }, onChangeField: newField => { - onChange(changeField(schema, filters, i, newField)); + onChange(changeField(schema, currentClassName, filters, i, newField)); }, onChangeConstraint: (newConstraint, prevCompareTo) => { - onChange(changeConstraint(schema, filters, i, newConstraint, prevCompareTo)); + onChange( + changeConstraint(schema, currentClassName, filters, i, newConstraint, prevCompareTo) + ); }, onChangeCompareTo: newCompare => { onChange(changeCompareTo(schema, filters, i, compareType, newCompare)); diff --git a/src/components/PushAudienceDialog/PushAudienceDialog.react.js b/src/components/PushAudienceDialog/PushAudienceDialog.react.js index 46e2444a6d..bd13bc2ce5 100644 --- a/src/components/PushAudienceDialog/PushAudienceDialog.react.js +++ b/src/components/PushAudienceDialog/PushAudienceDialog.react.js @@ -118,14 +118,14 @@ export default class PushAudienceDialog extends React.Component { this.setState({ saveForFuture: value }); } - fetchAudienceSize() { + async fetchAudienceSize() { if (!this.context) { //so we don't break the PIG demo return; } let query = {}; - const parseQuery = queryFromFilters('_Installation', this.state.filters); + const parseQuery = await queryFromFilters('_Installation', this.state.filters); if (parseQuery && parseQuery.toJSON()) { query = parseQuery.toJSON().where || {}; diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index bfe014c123..7c6aab202b 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -101,9 +101,13 @@ class Browser extends DashboardView { rowCheckboxDragging: false, draggedRowSelection: false, + + classes: {}, + AllclassesSchema: {} }; this.addLocation = this.addLocation.bind(this); + this.AllclassesSchema = this.getAllclassesSchema.bind(this); this.removeLocation = this.removeLocation.bind(this); this.prefetchData = this.prefetchData.bind(this); this.fetchData = this.fetchData.bind(this); @@ -223,6 +227,9 @@ class Browser extends DashboardView { this.prefetchData(nextProps, nextContext); } if (!nextProps.params.className && nextProps.schema.data.get('classes')) { + const t = nextProps.schema.data.get('classes'); + this.classes = Object.keys(t.toObject()); + this.AllclassesSchema = this.getAllclassesSchema(this.classes ,nextProps.schema.data.get('classes')); this.redirectToFirstClass(nextProps.schema.data.get('classes'), nextContext); } } @@ -751,6 +758,23 @@ class Browser extends DashboardView { } } + getAllclassesSchema(Allclasses , allclassesdata) { + const schemaSimplifiedData = {}; + Allclasses.forEach((className) => { + const classSchema = allclassesdata.get(className); + if (classSchema) { + schemaSimplifiedData[className] = {}; + classSchema.forEach(({ type, targetClass }, col) => { + schemaSimplifiedData[className][col] = { + type, + targetClass, + }; + }); + } + }); + return schemaSimplifiedData; + } + async refresh() { const relation = this.state.relation; const prevFilters = this.state.filters || new List(); @@ -773,9 +797,9 @@ class Browser extends DashboardView { } } - async fetchParseData(source, filters) { + async fetchParseData(source, filters, AllclassesSchema) { const { useMasterKey } = this.state; - const query = queryFromFilters(source, filters); + const query = await queryFromFilters(source, filters, AllclassesSchema); const sortDir = this.state.ordering[0] === '-' ? '-' : '+'; const field = this.state.ordering.substr(sortDir === '-' ? 1 : 0); @@ -787,7 +811,6 @@ class Browser extends DashboardView { query.limit(MAX_ROWS_FETCHED); this.excludeFields(query, source); - let promise = query.find({ useMasterKey }); let isUnique = false; let uniqueField = null; @@ -816,15 +839,15 @@ class Browser extends DashboardView { } } - async fetchParseDataCount(source, filters) { - const query = queryFromFilters(source, filters); + async fetchParseDataCount(source, filters,AllclassesSchema) { + const query = await queryFromFilters(source, filters,AllclassesSchema); const { useMasterKey } = this.state; const count = await query.count({ useMasterKey }); return count; } async fetchData(source, filters = new List()) { - const data = await this.fetchParseData(source, filters); + const data = await this.fetchParseData(source, filters,this.AllclassesSchema); const filteredCounts = { ...this.state.filteredCounts }; if (filters.size > 0) { if (this.state.isUnique) { @@ -860,13 +883,13 @@ class Browser extends DashboardView { return await this.context.getRelationCount(relation); } - fetchNextPage() { + async fetchNextPage() { if (!this.state.data || this.state.isUnique) { return null; } const className = this.props.params.className; const source = this.state.relation || className; - let query = queryFromFilters(source, this.state.filters); + let query = await queryFromFilters(source, this.state.filters); if (this.state.ordering !== '-createdAt') { // Construct complex pagination query const equalityQuery = queryFromFilters(source, this.state.filters); @@ -1940,6 +1963,7 @@ class Browser extends DashboardView { onMouseDownRowCheckBox={this.onMouseDownRowCheckBox} onMouseUpRowCheckBox={this.onMouseUpRowCheckBox} onMouseOverRowCheckBox={this.onMouseOverRowCheckBox} + classes={this.classes} /> ); } diff --git a/src/dashboard/Data/Browser/BrowserToolbar.react.js b/src/dashboard/Data/Browser/BrowserToolbar.react.js index ff3504ad57..162cda2f4c 100644 --- a/src/dashboard/Data/Browser/BrowserToolbar.react.js +++ b/src/dashboard/Data/Browser/BrowserToolbar.react.js @@ -74,6 +74,8 @@ const BrowserToolbar = ({ toggleMasterKeyUsage, selectedData, + Allclasses, + AllclassesSchema, }) => { const selectionLength = Object.keys(selection).length; const isPendingEditCloneRows = editCloneRows && editCloneRows.length > 0; @@ -225,6 +227,32 @@ const BrowserToolbar = ({ }); } + Allclasses.forEach(className => { + const classSchema = schema.data.get('classes').get(className); + + if (classSchema) { + schemaSimplifiedData[className] = {}; + + classSchema.forEach(({ type, targetClass }, col) => { + schemaSimplifiedData[className][col] = { + type, + targetClass, + }; + + columns[col] = { type, targetClass }; + + if (col === 'objectId' || (isUnique && col !== uniqueField)) { + return; + } + if ((type === 'Pointer' && targetClass === '_User') || type === 'Array') { + userPointers.push(col); + } + }); + } else { + console.log(`Class ${className} not found in schema.`); + } + }); + const clpDialogRef = useRef(null); const protectedDialogRef = useRef(null); const loginDialogRef = useRef(null); @@ -232,7 +260,6 @@ const BrowserToolbar = ({ const showCLP = () => clpDialogRef.current.handleOpen(); const showProtected = () => protectedDialogRef.current.handleOpen(); const showLogin = () => loginDialogRef.current.handleOpen(); - return ( {onAddRow &&
} {enableSecurityDialog ? ( @@ -383,14 +412,14 @@ const BrowserToolbar = ({