Skip to content

Commit 68ec513

Browse files
committed
property mapping validation #48
1 parent 3e79f8a commit 68ec513

File tree

6 files changed

+146
-23
lines changed

6 files changed

+146
-23
lines changed

src/main.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -507,15 +507,15 @@ export default class MediaDbPlugin extends Plugin {
507507
for (const defaultProperty of defaultPropertyMappingModel.properties) {
508508
let newProperty = newPropertyMappingModel.properties.find(x => x.property === defaultProperty.property);
509509
if (newProperty === undefined) {
510+
// default property is an instance
510511
newProperties.push(defaultProperty);
511512
} else {
512-
newProperties.push(newProperty);
513+
// newProperty is just an object and take locked status from default property
514+
newProperties.push(new PropertyMapping(newProperty.property, newProperty.newProperty, newProperty.mapping, defaultProperty.locked));
513515
}
514516
}
515517

516-
newPropertyMappingModel.properties = newProperties;
517-
518-
newPropertyMappings.push(newPropertyMappingModel);
518+
newPropertyMappings.push(new PropertyMappingModel(newPropertyMappingModel.type, newProperties));
519519
}
520520
}
521521
loadedSettings.propertyMappingModels = newPropertyMappings;

src/settings/PropertyMapper.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,23 @@ export class PropertyMapper {
1616
* @param obj
1717
*/
1818
convertObject(obj: object): object {
19+
console.log('test1');
20+
1921
if (!obj.hasOwnProperty('type')) {
2022
return obj;
2123
}
2224

25+
console.log('test2');
2326
// @ts-ignore
24-
if (MEDIA_TYPES.contains(obj.type)) {
27+
console.log(obj.type);
28+
29+
// @ts-ignore
30+
if (MEDIA_TYPES.filter(x => x.toString() == obj.type).length < 1) {
2531
return obj;
2632
}
2733

34+
console.log('test3');
35+
2836
// @ts-ignore
2937
const propertyMappings = this.plugin.settings.propertyMappingModels.find(x => x.type === obj.type).properties;
3038

src/settings/PropertyMapping.ts

Lines changed: 103 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {containsOnlyLettersAndUnderscores} from '../utils/Utils';
1+
import {containsOnlyLettersAndUnderscores, PropertyMappingNameConflictError, PropertyMappingValidationError} from '../utils/Utils';
22
import {MediaType} from '../utils/MediaType';
33

44
export enum PropertyMappingOption {
@@ -9,9 +9,73 @@ export enum PropertyMappingOption {
99

1010
export const propertyMappingOptions = [PropertyMappingOption.Default, PropertyMappingOption.Map, PropertyMappingOption.Remove];
1111

12-
export interface PropertyMappingModel {
13-
type: MediaType,
14-
properties: PropertyMapping[],
12+
export class PropertyMappingModel {
13+
type: MediaType;
14+
properties: PropertyMapping[];
15+
16+
constructor(type: MediaType, properties?: PropertyMapping[]) {
17+
this.type = type;
18+
this.properties = properties ?? [];
19+
}
20+
21+
validate(): { res: boolean, err?: Error } {
22+
// check properties
23+
for (const property of this.properties) {
24+
const propertyValidation = property.validate();
25+
if (!propertyValidation.res) {
26+
return {
27+
res: false,
28+
err: propertyValidation.err,
29+
};
30+
}
31+
}
32+
33+
// check for name collisions
34+
for (const property of this.getMappedProperties()) {
35+
const propertiesWithSameTarget = this.getMappedProperties().filter(x => x.newProperty === property.newProperty);
36+
if (propertiesWithSameTarget.length === 0) {
37+
// if we get there, then something in this code is wrong
38+
} else if (propertiesWithSameTarget.length === 1) {
39+
// all good
40+
} else {
41+
// two or more properties are mapped to the same property
42+
return {
43+
res: false,
44+
err: new PropertyMappingNameConflictError(`Multiple remapped properties (${propertiesWithSameTarget.map(x => x.toString()).toString()}) may not share the same name.`),
45+
};
46+
}
47+
}
48+
// remapped properties may not have the same name as any original property
49+
for (const property of this.getMappedProperties()) {
50+
const propertiesWithSameTarget = this.properties.filter(x => x.newProperty === property.property);
51+
if (propertiesWithSameTarget.length === 0) {
52+
// all good
53+
} else {
54+
// a mapped property shares the same name with an original property
55+
return {
56+
res: false,
57+
err: new PropertyMappingNameConflictError(`Remapped property (${property}) may not share it's new name with an existing property.`),
58+
};
59+
}
60+
}
61+
62+
return {
63+
res: true,
64+
};
65+
}
66+
67+
getMappedProperties() {
68+
return this.properties.filter(x => x.mapping === PropertyMappingOption.Map);
69+
}
70+
71+
copy(): PropertyMappingModel {
72+
const copy = new PropertyMappingModel(this.type);
73+
for (const property of this.properties) {
74+
const propertyCopy = new PropertyMapping(property.property, property.newProperty, property.mapping, property.locked);
75+
copy.properties.push(propertyCopy);
76+
}
77+
return copy;
78+
}
1579
}
1680

1781
export class PropertyMapping {
@@ -49,16 +113,47 @@ export class PropertyMapping {
49113
*/
50114
}
51115

52-
validate(): string {
116+
validate(): { res: boolean, err?: Error } {
117+
// locked property may only be default
118+
if (this.locked) {
119+
if (this.mapping === PropertyMappingOption.Remove) {
120+
return {
121+
res: false,
122+
err: new PropertyMappingValidationError(`Error in property mapping "${this.toString()}": locked property may not be removed.`),
123+
};
124+
}
125+
if (this.mapping === PropertyMappingOption.Map) {
126+
return {
127+
res: false,
128+
err: new PropertyMappingValidationError(`Error in property mapping "${this.toString()}": locked property may not be remapped.`),
129+
};
130+
}
131+
}
132+
133+
if (this.mapping === PropertyMappingOption.Default) {
134+
return {res: true};
135+
}
136+
if (this.mapping === PropertyMappingOption.Remove) {
137+
return {res: true};
138+
}
139+
53140
if (!this.property || !containsOnlyLettersAndUnderscores(this.property)) {
54-
return `Error in conversion rule "${this.toString()}": property may not be empty and only contain letters and underscores.`;
141+
return {
142+
res: false,
143+
err: new PropertyMappingValidationError(`Error in property mapping "${this.toString()}": property may not be empty and only contain letters and underscores.`),
144+
};
55145
}
56146

57147
if (!this.newProperty || !containsOnlyLettersAndUnderscores(this.newProperty)) {
58-
return `Error in conversion rule "${this.toString()}": new property may not be empty and only contain letters and underscores.`;
148+
return {
149+
res: false,
150+
err: new PropertyMappingValidationError(`Error in property mapping "${this.toString()}": new property may not be empty and only contain letters and underscores.`),
151+
};
59152
}
60153

61-
return '';
154+
return {
155+
res: true,
156+
};
62157
}
63158

64159
toString(): string {

src/settings/PropertyMappingModelsComponent.svelte

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,15 @@
4949
margin: 0;
5050
}
5151
52-
.media-db-plugin-property-binding-to {
52+
.media-db-plugin-property-mapping-to {
5353
display: flex;
5454
align-items: center;
5555
}
56+
57+
.media-db-plugin-property-mapping-validation {
58+
color: var(--text-error);
59+
margin-bottom: 5px;
60+
}
5661
</style>
5762

5863
<div class="setting-item" style="display: flex; gap: 10px; flex-direction: column; align-items: stretch;">
@@ -81,18 +86,25 @@
8186

8287
{#if property.mapping === PropertyMappingOption.Map}
8388
<Icon iconName="arrow-right"/>
84-
<div class="media-db-plugin-property-binding-to">
89+
<div class="media-db-plugin-property-mapping-to">
8590
<input type="text" spellcheck="false" bind:value="{property.newProperty}">
8691
</div>
8792
{/if}
8893
{/if}
8994
</div>
90-
{ /each }
95+
{ /each }
9196
</div>
92-
<button class="mod-cta media-db-plugin-property-mappings-save-button" on:click={() => save(model)}>Save
97+
{ #if !model.validate().res }
98+
<div class="media-db-plugin-property-mapping-validation">
99+
{model.validate().err?.message}
100+
</div>
101+
{/if}
102+
<button
103+
class="media-db-plugin-property-mappings-save-button {model.validate().res ? 'mod-cta' : 'mod-muted'}"
104+
on:click={() => { if(model.validate().res) save(model) }}>Save
93105
</button>
94106
</div>
95-
{ /each }
107+
{ /each }
96108

97109
<pre>{JSON.stringify(models, null, 4)}</pre>
98110

src/settings/Settings.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,7 @@ export function getDefaultSettings(plugin: MediaDbPlugin): MediaDbPluginSettings
142142
// console.log(metadataObj);
143143
// console.log(model);
144144

145-
const propertyMappingModel: PropertyMappingModel = {
146-
type: mediaType,
147-
properties: [],
148-
};
145+
const propertyMappingModel: PropertyMappingModel = new PropertyMappingModel(mediaType);
149146

150147
for (const key of Object.keys(metadataObj)) {
151148
propertyMappingModel.properties.push(
@@ -486,7 +483,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
486483
new PropertyMappingModelsComponent({
487484
target: this.containerEl,
488485
props: {
489-
models: JSON.parse(JSON.stringify(this.plugin.settings.propertyMappingModels)),
486+
models: this.plugin.settings.propertyMappingModels.map(x => x.copy()),
490487
save: (model: PropertyMappingModel) => {
491488
let propertyMappingModels: PropertyMappingModel[] = [];
492489

0 commit comments

Comments
 (0)