Skip to content

Commit

Permalink
fix: array manipulaton in deeply nested propertiescloses: #645
Browse files Browse the repository at this point in the history
  • Loading branch information
wojtek-krysiak committed Oct 23, 2020
1 parent cb60200 commit 7f0bb71
Show file tree
Hide file tree
Showing 40 changed files with 543 additions and 140 deletions.
39 changes: 39 additions & 0 deletions example-app/src/employees/employee.entity.js
@@ -1,5 +1,33 @@
const mongoose = require('mongoose')

const Skill = new mongoose.Schema({
name: String,
level: {
type: String,
enum: ['junior', 'middle', 'senior'],
},
Profession: {
type: mongoose.SchemaTypes.ObjectId,
ref: 'Profession',
},
})

const OtherSchema = new mongoose.Schema({
name: String,
arrayed: {
type: [Skill],
},
})

const Skills = new mongoose.Schema({
softShills: [{
type: Skill,
}],
hardSkills: [{
type: Skill,
}],
})

const EmployeeSchema = new mongoose.Schema({
name: {
type: String,
Expand All @@ -16,10 +44,21 @@ const EmployeeSchema = new mongoose.Schema({
type: mongoose.SchemaTypes.ObjectId,
ref: 'Company',
},
interests: {
type: [String],
},
professions: [{
type: mongoose.SchemaTypes.ObjectId,
ref: 'Profession',
}],

Skills: {
type: Skills,
},

otherField: {
type: [OtherSchema],
},
})

const Employee = mongoose.model('Employee', EmployeeSchema)
Expand Down
2 changes: 1 addition & 1 deletion src/backend/decorators/property/property-decorator.spec.ts
Expand Up @@ -193,7 +193,7 @@ describe('PropertyDecorator', () => {
'isArray',
'custom',
'resourceId',
'path',
'propertyPath',
'isRequired',
'isVirtual',
'props',
Expand Down
22 changes: 11 additions & 11 deletions src/backend/decorators/property/property-decorator.ts
Expand Up @@ -3,7 +3,7 @@ import PropertyOptions from './property-options.interface'
import BaseResource from '../../adapters/resource/base-resource'
import BaseProperty, { PropertyType } from '../../adapters/property/base-property'
import ResourceDecorator from '../resource/resource-decorator'
import { PropertyPlace, PropertyJSON } from '../../../frontend/interfaces'
import { PropertyPlace, BasePropertyJSON } from '../../../frontend/interfaces'
import { overrideFromOptions } from './utils'

/**
Expand All @@ -23,7 +23,7 @@ class PropertyDecorator {
* This path serves as a key in {@link PropertyOptions} to identify which
* property has to be updated
*/
public path: string
public propertyPath: string

/**
* Indicates if given property has been created in AdminBro and hasn't been returned by the
Expand Down Expand Up @@ -63,7 +63,7 @@ class PropertyDecorator {
this.property = property
this._admin = admin
this._resource = resource
this.path = path || property.name()
this.propertyPath = path || property.name()
this.isVirtual = !!isVirtual
this.virtualSubProperties = []

Expand Down Expand Up @@ -123,7 +123,7 @@ class PropertyDecorator {
* @return {string}
*/
label(): string {
return this._admin.translateProperty(this.path, this._resource.id())
return this._admin.translateProperty(this.propertyPath, this._resource.id())
}

/**
Expand Down Expand Up @@ -153,7 +153,7 @@ class PropertyDecorator {
return values.map(val => ({
value: val,
label: this._admin.translateProperty(
`${this.path}.${val}`,
`${this.propertyPath}.${val}`,
this._resource.id(),
{ defaultValue: val },
),
Expand Down Expand Up @@ -246,7 +246,7 @@ class PropertyDecorator {
*
* @return {PropertyJSON}
*/
toJSON(where?: PropertyPlace): PropertyJSON {
toJSON(where?: PropertyPlace): BasePropertyJSON {
return {
isTitle: this.isTitle(),
isId: this.isId(),
Expand All @@ -256,7 +256,7 @@ class PropertyDecorator {
isRequired: this.isRequired(),
availableValues: this.availableValues(),
name: this.name(),
path: this.path,
propertyPath: this.propertyPath,
isDisabled: this.isDisabled(),
label: this.label(),
type: this.type(),
Expand All @@ -280,7 +280,7 @@ class PropertyDecorator {
*/
subProperties(): Array<PropertyDecorator> {
const dbSubProperties = this.property.subProperties().map((subProperty) => {
const path = `${this.path}.${subProperty.name()}`
const path = `${this.propertyPath}.${subProperty.name()}`
const decorated = new PropertyDecorator({
property: subProperty,
admin: this._admin,
Expand All @@ -301,14 +301,14 @@ class PropertyDecorator {
* Returns PropertyOptions passed by the user for a subProperty. Furthermore
* it changes property name to the nested property key.
*
* @param {BaseProperty} subProperty
* @param {String} propertyPath
* @return {PropertyOptions}
* @private
*/
private getOptionsForSubProperty(path: string): PropertyOptions {
private getOptionsForSubProperty(propertyPath: string): PropertyOptions {
const propertyOptions = (this._resource.options || {}).properties || {}
return {
...propertyOptions[path],
...propertyOptions[propertyPath],
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/backend/decorators/resource/resource-decorator.spec.ts
Expand Up @@ -159,7 +159,7 @@ describe('ResourceDecorator', function () {
const decoratedProperty = decorator.getPropertyByKey(path) as PropertyDecorator

expect(decoratedProperty).to.be.an.instanceof(PropertyDecorator)
expect(decoratedProperty.path).to.eq(path)
expect(decoratedProperty.propertyPath).to.eq(path)
})

it('returns nested property under 2 level nested mixed', function () {
Expand All @@ -171,7 +171,7 @@ describe('ResourceDecorator', function () {
const decoratedProperty = decorator.getPropertyByKey(path) as PropertyDecorator

expect(decoratedProperty).to.be.an.instanceof(PropertyDecorator)
expect(decoratedProperty.path).to.eq(path)
expect(decoratedProperty.propertyPath).to.eq(path)
})

it('returns property when it is an array', function () {
Expand All @@ -182,7 +182,7 @@ describe('ResourceDecorator', function () {
const decoratedProperty = decorator.getPropertyByKey(path) as PropertyDecorator

expect(decoratedProperty).to.be.an.instanceof(PropertyDecorator)
expect(decoratedProperty.path).to.eq(arrayProperty.path())
expect(decoratedProperty.propertyPath).to.eq(arrayProperty.path())
})

it('returns property when it is an nested array', function () {
Expand All @@ -196,7 +196,7 @@ describe('ResourceDecorator', function () {
const decoratedProperty = decorator.getPropertyByKey(path) as PropertyDecorator

expect(decoratedProperty).to.be.an.instanceof(PropertyDecorator)
expect(decoratedProperty.path).to.eq([arrayProperty.path(), nested1Property.path()].join('.'))
expect(decoratedProperty.propertyPath).to.eq([arrayProperty.path(), nested1Property.path()].join('.'))
})
})

Expand Down
Expand Up @@ -74,7 +74,7 @@ const organizeNestedProperties = (
// reverse because we start by by finding from the longest path
// and removes itself. (skips arrays)
// changes 'root.nested.0.nested1' to [root.nested', 'root']
const parts = pathToParts(property.path, { skipArrayIndexes: true }).reverse().splice(1)
const parts = pathToParts(property.propertyPath, { skipArrayIndexes: true }).reverse().splice(1)
if (parts.length) {
const mixedPropertyPath = parts.find(part => (
properties[part] && properties[part].type() === 'mixed'
Expand Down
4 changes: 2 additions & 2 deletions src/backend/decorators/resource/utils/find-sub-property.ts
Expand Up @@ -16,9 +16,9 @@ export const findSubProperty = (
): PropertyDecorator | null => {
const subProperties = rootProperty.subProperties()
const foundPath = pathParts.find(path => (
subProperties.find(supProperty => supProperty.path === path)))
subProperties.find(supProperty => supProperty.propertyPath === path)))
if (foundPath) {
const subProperty = subProperties.find(supProperty => supProperty.path === foundPath)
const subProperty = subProperties.find(supProperty => supProperty.propertyPath === foundPath)
if (subProperty && foundPath !== pathParts[pathParts.length - 1]) {
// if foundPath is not the last (full) path - checkout recursively all subProperties
return findSubProperty(pathParts, subProperty)
Expand Down
Expand Up @@ -18,7 +18,7 @@ export const flatSubProperties = (
): Record<string, PropertyJSON> => (
rootProperty.subProperties().reduce((subMemo, subProperty) => ({
...subMemo,
[subProperty.path]: subProperty.toJSON(),
[subProperty.propertyPath]: subProperty.toJSON(),
...flatSubProperties(subProperty),
}), {})
)
2 changes: 1 addition & 1 deletion src/backend/utils/populator/populate-property.spec.ts
Expand Up @@ -25,7 +25,7 @@ describe('populateProperty', () => {
property.resource.returns(resourceDecorator as unknown as ResourceDecorator)
property.reference.returns(referenceResource as unknown as BaseResource)
property.property = { reference: 'someRawReference' } as unknown as BaseProperty
property.path = path
property.propertyPath = path
})

afterEach(() => {
Expand Down
10 changes: 5 additions & 5 deletions src/backend/utils/populator/populate-property.ts
Expand Up @@ -29,7 +29,7 @@ export async function populateProperty(
if (!referencedResource) {
throw new Error([
`There is no reference resource named: "${property.property.reference}"`,
`for property: "${decoratedResource.id()}.properties.${property.path}"`,
`for property: "${decoratedResource.id()}.properties.${property.propertyPath}"`,
].join('\n'))
}

Expand All @@ -41,7 +41,7 @@ export async function populateProperty(
// first, we create externalIdsMap[1] = null where 1 is userId. This make keys unique and assign
// nulls to each of them
const externalIdsMap = records.reduce((memo, baseRecord) => {
const foreignKeyValue = baseRecord.get(property.path)
const foreignKeyValue = baseRecord.get(property.propertyPath)
// array properties returns arrays so we have to take the all into consideration
if (Array.isArray(foreignKeyValue) && property.isArray()) {
return foreignKeyValue.reduce((arrayMemo, valueInArray) => ({
Expand Down Expand Up @@ -85,17 +85,17 @@ export async function populateProperty(
return records.map((record) => {
// we set record.populated['userId'] = externalIdsMap[record.param('userId)]
// but this can also be an array - we have to check it
const foreignKeyValue = record.get(property.path)
const foreignKeyValue = record.get(property.propertyPath)

if (Array.isArray(foreignKeyValue)) {
foreignKeyValue.forEach((foreignKeyValueItem, index) => {
record.populate(
[property.path, index].join(DELIMITER),
[property.propertyPath, index].join(DELIMITER),
externalIdsMap[foreignKeyValueItem],
)
})
} else if (typeof foreignKeyValue === 'string' || typeof foreignKeyValue === 'number') {
record.populate(property.path, externalIdsMap[foreignKeyValue])
record.populate(property.propertyPath, externalIdsMap[foreignKeyValue])
}

return record
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/components/actions/edit.tsx
Expand Up @@ -62,7 +62,7 @@ const Edit: FC<ActionProps> = (props) => {
/>
)) : resource.editProperties.map(property => (
<PropertyType
key={property.path}
key={property.propertyPath}
where="edit"
onChange={handleChange}
property={property}
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/components/actions/new.tsx
Expand Up @@ -66,7 +66,7 @@ const New: FC<ActionProps> = (props) => {
/>
)) : resource.editProperties.map(property => (
<PropertyType
key={property.path}
key={property.propertyPath}
where="edit"
onChange={handleChange}
property={property}
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/components/actions/show.tsx
Expand Up @@ -30,7 +30,7 @@ const Show: React.FC<ActionProps> = (props) => {
/>
)) : properties.map(property => (
<PropertyType
key={property.path}
key={property.propertyPath}
where="show"
property={property}
resource={resource}
Expand Down
Expand Up @@ -2,7 +2,7 @@ import React from 'react'
import * as DesignSystem from '@admin-bro/design-system'
import { ActionProps } from '../action.props'
import PropertyType from '../../property-type'
import { PropertyPlace } from '../../../interfaces/property-json.interface'
import { PropertyPlace } from '../../../interfaces/property-json/property-json.interface'
import { ParsedLayoutElement } from '../../../../backend/utils/layout-element-parser'
import { BasePropertyProps } from '../../property-type/base-property-props'

Expand Down Expand Up @@ -47,9 +47,9 @@ export const LayoutElementRenderer: React.FC<Props> = (props) => {
return (
<Component {...other as any}>
{properties.map(property => (
<DesignSystem.Box flexGrow={1} key={property.path}>
<DesignSystem.Box flexGrow={1} key={property.propertyPath}>
<PropertyType
key={property.path}
key={property.propertyPath}
where={where}
property={property}
resource={resource}
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/components/app/filter-drawer.tsx
Expand Up @@ -105,7 +105,7 @@ export const FilterDrawer: React.FC<FilterProps> = (props) => {
<Box my="x3">
{properties.map(property => (
<PropertyType
key={property.path}
key={property.propertyPath}
where="filter"
onChange={handleChange}
property={property}
Expand Down
8 changes: 4 additions & 4 deletions src/frontend/components/app/records-table/property-header.tsx
@@ -1,15 +1,15 @@
import React from 'react'
import { TableCell } from '@admin-bro/design-system'

import { PropertyJSON } from '../../../interfaces'
import { BasePropertyJSON } from '../../../interfaces'
import SortLink from '../sort-link'

export type PropertyHeaderProps = {
property: PropertyJSON;
property: BasePropertyJSON;
/**
* Property which should be treated as main property.
*/
titleProperty: PropertyJSON;
titleProperty: BasePropertyJSON;
/**
* currently selected direction. Either 'asc' or 'desc'.
*/
Expand All @@ -25,7 +25,7 @@ export type PropertyHeaderProps = {
export const PropertyHeader: React.FC<PropertyHeaderProps> = (props) => {
const { property, titleProperty, display } = props

const isMain = property.path === titleProperty.path
const isMain = property.propertyPath === titleProperty.propertyPath

return (
<TableCell
Expand Down
6 changes: 3 additions & 3 deletions src/frontend/components/app/records-table/record-in-list.tsx
Expand Up @@ -102,15 +102,15 @@ export const RecordInList: React.FC<RecordInListProps> = (props) => {
{resource.listProperties.map(property => (
<TableCell
style={{ cursor: 'pointer' }}
key={property.path}
data-property-name={property.path}
key={property.propertyPath}
data-property-name={property.propertyPath}
display={display(property.isTitle)}
>
{isLoading ? (
<Placeholder style={{ height: 14 }} />
) : (
<PropertyType
key={property.path}
key={property.propertyPath}
where="list"
property={property}
resource={resource}
Expand Down

0 comments on commit 7f0bb71

Please sign in to comment.