Skip to content
This repository has been archived by the owner on Nov 28, 2019. It is now read-only.

Commit

Permalink
fix: inheritance
Browse files Browse the repository at this point in the history
  • Loading branch information
elderapo committed Feb 13, 2019
1 parent 200aa4d commit aba79f4
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 21 deletions.
56 changes: 36 additions & 20 deletions src/ProtobufLiteMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,29 +169,13 @@ export class ProtobufLiteMetadata {
.filter(p => p !== this.MessageClass.prototype);

let fieldIndex = 0;
let alreadyUsedPropertyKeys: Map<string, boolean> = new Map();

const addFields = (fields: IFieldInfo[]) => {
for (let field of fields) {
if (alreadyUsedPropertyKeys.has(field.propertyKey)) {
throw new Error(`Parent class field was most likely overwrited by child class!`);
}

t.add(new Field(field.propertyKey, fieldIndex++, field.prototype, field.rule));

alreadyUsedPropertyKeys.set(field.propertyKey, true);
}
};

for (let prototype of prototypes) {
const mt = getMetadataObject(prototype.constructor);
const fields = mt.getFieldsInfo();
const collectedFieldsInfo = this.collectFieldsInfo();

addFields(fields);
for (let fieldInfo of collectedFieldsInfo) {
t.add(new Field(fieldInfo.propertyKey, fieldIndex++, fieldInfo.prototype, fieldInfo.rule));
}

addFields(this.fieldsInfo);

for (let item of this.childTypes) {
const metadata = getMetadataObject(item.MessageClass);
t.add(metadata.getProto());
Expand Down Expand Up @@ -288,10 +272,42 @@ export class ProtobufLiteMetadata {
}
}

public getFieldsInfo() {
private getFieldsInfo() {
return this.fieldsInfo;
}

public collectFieldsInfo() {
const prototypes = getPrototypeChain(this.MessageClass)
.reverse()
.filter(p => p !== this.MessageClass.prototype);

const alreadyUsedPropertyKeys: Map<string, boolean> = new Map();
const fieldsInfo: IFieldInfo[] = [];

const addFields = (fields: IFieldInfo[]) => {
for (let field of fields) {
if (alreadyUsedPropertyKeys.has(field.propertyKey)) {
throw new Error(`Parent class field was most likely overwrited by child class!`);
}

fieldsInfo.push(field);

alreadyUsedPropertyKeys.set(field.propertyKey, true);
}
};

for (let prototype of prototypes) {
const mt = getMetadataObject(prototype.constructor);
const fields = mt.getFieldsInfo();

addFields(fields);
}

addFields(this.fieldsInfo);

return fieldsInfo;
}

private getMessageClassName() {
return this.MessageClass.name;
}
Expand Down
2 changes: 1 addition & 1 deletion src/calculateProtobufLiteClassChecksum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const getFieldInfo = (Class: Constructable<Object>): IFieldInfo[] => {

const metadataObject = getMetadataObject(Class);

return metadataObject.getFieldsInfo();
return metadataObject.collectFieldsInfo();
};

export const calculateProtobufLiteClassChecksum = (Class: Constructable<Object>): string => {
Expand Down
120 changes: 120 additions & 0 deletions test/calculateProtobufLiteClassChecksum.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,94 @@ describe("getFieldInfo", () => {
{ propertyKey: "dates", prototype: "bytes", rule: "repeated" }
]);
});

it("should return correct fields for inhreited classes v1", () => {
class Parent {
@ProtobufLiteProperty()
someField: string;
}

class Child extends Parent {
@ProtobufLiteProperty()
someOtherField: string;
}

expect(getFieldInfo(Child)).toMatchObject([
{ propertyKey: "someField", prototype: "string", rule: "required" },
{ propertyKey: "someOtherField", prototype: "string", rule: "required" }
]);
});

it("should return correct fields for inhreited classes v2", () => {
class C1 {
@ProtobufLiteProperty()
c1: string;
}

class C2 extends C1 {
@ProtobufLiteProperty()
c2: string;
}

class C3 extends C2 {
@ProtobufLiteProperty()
c3: string;
}

class C4 extends C3 {
@ProtobufLiteProperty()
c4: string;
}

class C5 extends C4 {
@ProtobufLiteProperty()
c5: string;
}

expect(getFieldInfo(C1)).toMatchObject([
{ propertyKey: "c1", prototype: "string", rule: "required" }
]);

expect(getFieldInfo(C2)).toMatchObject([
{ propertyKey: "c1", prototype: "string", rule: "required" },
{ propertyKey: "c2", prototype: "string", rule: "required" }
]);

expect(getFieldInfo(C3)).toMatchObject([
{ propertyKey: "c1", prototype: "string", rule: "required" },
{ propertyKey: "c2", prototype: "string", rule: "required" },
{ propertyKey: "c3", prototype: "string", rule: "required" }
]);

expect(getFieldInfo(C4)).toMatchObject([
{ propertyKey: "c1", prototype: "string", rule: "required" },
{ propertyKey: "c2", prototype: "string", rule: "required" },
{ propertyKey: "c3", prototype: "string", rule: "required" },
{ propertyKey: "c4", prototype: "string", rule: "required" }
]);

expect(getFieldInfo(C5)).toMatchObject([
{ propertyKey: "c1", prototype: "string", rule: "required" },
{ propertyKey: "c2", prototype: "string", rule: "required" },
{ propertyKey: "c3", prototype: "string", rule: "required" },
{ propertyKey: "c4", prototype: "string", rule: "required" },
{ propertyKey: "c5", prototype: "string", rule: "required" }
]);
});

it("should throw if child overwrites parent fields", () => {
class Parent {
@ProtobufLiteProperty()
someField: string;
}

class Child extends Parent {
@ProtobufLiteProperty()
someField: string;
}

expect(() => getFieldInfo(Child)).toThrowError();
});
});

describe("calculateProtobufLiteClassChecksum", () => {
Expand Down Expand Up @@ -286,4 +374,36 @@ describe("calculateProtobufLiteClassChecksum", () => {

expect(calculateProtobufLiteClassChecksum(C3)).toBe("d9d1b2e83e64f6c3f9d1ebdcc5499d04a781b89e");
});

it("should correctly calculate checksums for inherited classes", () => {
class Parent {
@ProtobufLiteProperty()
parent: string;
}

class Child extends Parent {
@ProtobufLiteProperty()
child: string;
}

class ParentAndChild {
@ProtobufLiteProperty()
parent: string;

@ProtobufLiteProperty()
child: string;
}

expect(calculateProtobufLiteClassChecksum(Parent)).not.toBe(
calculateProtobufLiteClassChecksum(Child)
);

expect(calculateProtobufLiteClassChecksum(Parent)).not.toBe(
calculateProtobufLiteClassChecksum(ParentAndChild)
);

expect(calculateProtobufLiteClassChecksum(Child)).toBe(
calculateProtobufLiteClassChecksum(ParentAndChild)
);
});
});

0 comments on commit aba79f4

Please sign in to comment.