Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35,087 changes: 35,018 additions & 69 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"@angular/platform-browser": "^11.0.7",
"@angular/platform-browser-dynamic": "^11.0.7",
"@angular/router": "^11.0.7",
"@hypertrace/hyperdash": "^1.1.2",
"@hypertrace/hyperdash": "^1.2.1",
"core-js": "^3.8.3",
"lodash-es": "^4.17.20",
"rxjs": "^6.6.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,18 @@ import { Injectable } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import {
ARRAY_PROPERTY,
BOOLEAN_PROPERTY,
Deserializer,
JsonPrimitive,
LogMessage,
ModelPropertyTypeRegistrationInformation,
NUMBER_PROPERTY
ModelPropertyTypeRegistrationInformation
} from '@hypertrace/hyperdash';
import { DeserializationManagerService } from '../injectable-wrappers/deserialization/deserialization-manager.service';
import { LoggerService } from '../injectable-wrappers/logger.service';
import { ModelLibraryService } from '../injectable-wrappers/model-library.service';
import { ModelManagerService } from '../injectable-wrappers/model-manager.service';
import { ModelPropertyTypeLibraryService } from '../injectable-wrappers/model-property-type-library.service';
import { ModelPropertyValidatorService } from '../injectable-wrappers/model-property-validator.service';
import { SerializationManagerService } from '../injectable-wrappers/serialization/serialization-manager.service';
import { VariableManagerService } from '../injectable-wrappers/variable-manager.service';
import { MODEL_PROPERTY_TYPES } from '../module/dashboard-core.module';
import { DASHBOARD_DESERIALIZERS, MODEL_PROPERTY_TYPES } from '../module/dashboard-core.module';
import { DefaultConfigurationService } from './default-configuration.service';

describe('Default configuration service', () => {
Expand All @@ -29,6 +27,11 @@ describe('Default configuration service', () => {
provide: MODEL_PROPERTY_TYPES,
useValue: [{ type: 'test-property' }, TestPropertyTypeProvider],
multi: true
},
{
provide: DASHBOARD_DESERIALIZERS,
useValue: [TestDeserializer],
multi: true
}
]
});
Expand All @@ -48,92 +51,6 @@ describe('Default configuration service', () => {
logger.warn = jest.fn();
});

test('correctly configures deserialization', () => {
const deserializationManager = TestBed.inject(DeserializationManagerService);
const modelLibrary = TestBed.inject(ModelLibraryService);

TestBed.inject(ModelPropertyValidatorService).setStrictSchema(false);
const testModel = class ModelClass {
public constructor(public prop: unknown) {}
};

modelLibrary.registerModelClass(testModel, { type: 'test-model' });
modelLibrary.registerModelProperty(testModel, 'prop', {
type: BOOLEAN_PROPERTY.type,
key: 'prop'
});

// Should throw until we configure the deserialization
expect(() => deserializationManager.deserialize({ type: 'test-model', prop: false })).toThrow();

defaultConfigurationService.configure();
expect(deserializationManager.deserialize({ type: 'test-model', prop: false })).toEqual(new testModel(false));
expect(deserializationManager.deserialize({ type: 'test-model', prop: [false] })).toEqual(new testModel([false]));
expect(deserializationManager.deserialize({ type: 'test-model', prop: { nested: false } })).toEqual(
new testModel({ nested: false })
);

expect(
deserializationManager.deserialize({
type: 'test-model',
prop: {
type: 'test-model',
prop: 'two models'
}
})
).toEqual(new testModel(new testModel('two models')));

expect(
deserializationManager.deserialize({
type: 'test-model',
prop: {
nested: {
type: 'test-model',
prop: 'object sandwich'
}
}
})
).toEqual(new testModel({ nested: new testModel('object sandwich') }));
});

test('correctly configures deserialization and setting of variables', () => {
const deserializationManager = TestBed.inject(DeserializationManagerService);
const modelLibrary = TestBed.inject(ModelLibraryService);

const testModel = class ModelClass {
public constructor(public prop?: number) {}
};

modelLibrary.registerModelClass(testModel, { type: 'test-model' });
modelLibrary.registerModelProperty(testModel, 'prop', {
type: NUMBER_PROPERTY.type,
key: 'prop',
required: false
});

defaultConfigurationService.configure();

const deserializedModel = deserializationManager.deserialize<object>({
type: 'test-model',
// tslint:disable-next-line:no-invalid-template-strings
prop: '${test}'
});

expect(deserializedModel).toEqual(new testModel());

TestBed.inject(VariableManagerService).set('test', 42, deserializedModel);

expect(deserializedModel).toEqual(new testModel(42));
});

test('should throw if attempting to configure twice', () => {
defaultConfigurationService.configure();

expect(() => defaultConfigurationService.configure()).toThrow(
'Default Configuration Service cannot be configured twice'
);
});

test('correctly configures serialization', () => {
const serializationManager = TestBed.inject(SerializationManagerService);
const modelLibrary = TestBed.inject(ModelLibraryService);
Expand Down Expand Up @@ -173,9 +90,26 @@ describe('Default configuration service', () => {
const propertyTypeLibrary = TestBed.inject(ModelPropertyTypeLibraryService);
propertyTypeLibrary.registerPropertyType = jest.fn();
defaultConfigurationService.configure();
expect(propertyTypeLibrary.registerPropertyType).toHaveBeenCalledWith({ type: 'test-property' });

expect(propertyTypeLibrary.registerPropertyType).toHaveBeenCalledTimes(2);
expect(propertyTypeLibrary.registerPropertyType).toHaveBeenCalledWith({ type: 'test-property' });
expect(propertyTypeLibrary.registerPropertyType).toHaveBeenCalledWith(expect.any(TestPropertyTypeProvider));

defaultConfigurationService.configure();
// Should not be called a third time
expect(propertyTypeLibrary.registerPropertyType).toHaveBeenCalledTimes(2);
});

test('registers provided deserializers', () => {
const deserializationManager = TestBed.inject(DeserializationManagerService);
deserializationManager.registerDeserializer = jest.fn();
defaultConfigurationService.configure();
expect(deserializationManager.registerDeserializer).toHaveBeenCalledTimes(1);
expect(deserializationManager.registerDeserializer).toHaveBeenCalledWith(expect.any(TestDeserializer));

defaultConfigurationService.configure();
// Should not be called a second time
expect(deserializationManager.registerDeserializer).toHaveBeenCalledTimes(1);
});
});

Expand All @@ -185,3 +119,15 @@ describe('Default configuration service', () => {
class TestPropertyTypeProvider implements ModelPropertyTypeRegistrationInformation {
public readonly type: string = 'test-prop-provider';
}

@Injectable({
providedIn: 'root'
})
class TestDeserializer implements Deserializer<string, string> {
public canDeserialize(json: JsonPrimitive): json is string {
return typeof json === 'string';
}
public deserialize(json: string): string {
return json.toUpperCase();
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
import { Inject, Injectable, Injector, Type } from '@angular/core';
import { ModelPropertyTypeRegistrationInformation } from '@hypertrace/hyperdash';
import { AbstractType, Inject, Injectable, Injector, Type } from '@angular/core';
import { Deserializer, ModelPropertyTypeRegistrationInformation } from '@hypertrace/hyperdash';
import { flatten, uniq } from 'lodash-es';
import { DefaultModelApiBuilderService } from '../injectable-wrappers/default-model-api-builder.service';
import { ArrayDeserializerService } from '../injectable-wrappers/deserialization/array-deserializer.service';
import { DeserializationManagerService } from '../injectable-wrappers/deserialization/deserialization-manager.service';
import { ModelDeserializerService } from '../injectable-wrappers/deserialization/model-deserializer.service';
import { ObjectDeserializerService } from '../injectable-wrappers/deserialization/object-deserializer.service';
import { PrimitiveDeserializerService } from '../injectable-wrappers/deserialization/primitive-deserializer.service';
import { VariableDeserializerService } from '../injectable-wrappers/deserialization/variable-deserializer.service';
import { ModelEventInstallerService } from '../injectable-wrappers/model-event-installer.service';
import { ModelManagerService } from '../injectable-wrappers/model-manager.service';
import { ModelPropertyTypeLibraryService } from '../injectable-wrappers/model-property-type-library.service';
Expand All @@ -18,7 +13,7 @@ import { PrimitiveSerializerService } from '../injectable-wrappers/serialization
import { SerializationManagerService } from '../injectable-wrappers/serialization/serialization-manager.service';
import { VariableSerializerService } from '../injectable-wrappers/serialization/variable-serializer.service';
import { ModelInjectService } from '../model/decorators/model-inject.service';
import { MODEL_PROPERTY_TYPES } from '../module/dashboard-core.module';
import { DASHBOARD_DESERIALIZERS, MODEL_PROPERTY_TYPES } from '../module/dashboard-core.module';

/**
* Configures dashboard services for default behavior
Expand All @@ -28,14 +23,10 @@ import { MODEL_PROPERTY_TYPES } from '../module/dashboard-core.module';
})
export class DefaultConfigurationService {
private configured: boolean = false;
private readonly registeredObjects: WeakSet<object> = new WeakSet();

public constructor(
private readonly deserializationManager: DeserializationManagerService,
private readonly objectDeserializer: ObjectDeserializerService,
private readonly arrayDeserializer: ArrayDeserializerService,
private readonly primitiveDeserializer: PrimitiveDeserializerService,
private readonly modelDeserializer: ModelDeserializerService,
private readonly variableDeserializer: VariableDeserializerService,
private readonly modelManager: ModelManagerService,
private readonly defaultModelApiBuilder: DefaultModelApiBuilderService,
private readonly serializationManager: SerializationManagerService,
Expand All @@ -48,35 +39,43 @@ export class DefaultConfigurationService {
private readonly modelEventInstaller: ModelEventInstallerService,
private readonly modelInjectService: ModelInjectService,
private readonly injector: Injector,
@Inject(MODEL_PROPERTY_TYPES) private readonly propertyTypes: PropertyTypeRegistration[][]
@Inject(MODEL_PROPERTY_TYPES) private readonly propertyTypes: PropertyTypeRegistration[][],
@Inject(DASHBOARD_DESERIALIZERS)
private readonly deserializers: (Type<Deserializer> | AbstractType<Deserializer>)[][]
) {}

/**
* Does the configuration. This should be called during application bootstrap.
* Does the configuration. This should be called during application bootstrap. Later calls will
* do incremental configuration.
*
* @throws Error if configure is called more than once
*/
public configure(): void {
this.registerPropertyTypes();
this.registerDeserializers();

if (this.configured) {
throw new Error('Default Configuration Service cannot be configured twice');
return;
}
this.configured = true;
this.registerPropertyTypes();
this.configureDeserialization();
// Beyond here, code should only be invoked once
this.configureSerialization();
this.registerModelDecorators();
this.configureModelApiBuilder();
}

private configureDeserialization(): void {
this.deserializationManager.registerDeserializer(this.variableDeserializer);
this.deserializationManager.registerDeserializer(this.primitiveDeserializer);
this.deserializationManager.registerDeserializer(this.modelDeserializer);
this.deserializationManager.registerDeserializer(this.arrayDeserializer);
this.deserializationManager.registerDeserializer(this.objectDeserializer);
private registerDeserializers(): void {
uniq(flatten(this.deserializers)).forEach(deserializerClass => {
if (this.registeredObjects.has(deserializerClass)) {
return;
}
this.registeredObjects.add(deserializerClass);

this.deserializationManager.registerDeserializer(this.injector.get(deserializerClass));
});
}

private configureSerialization(): void {
// TODO - expose customizing serializers similar to deserializers
this.serializationManager.registerSerializer(this.variableSerializer);
this.serializationManager.registerSerializer(this.primitiveSerializer);
this.serializationManager.registerSerializer(this.modelSerializer);
Expand All @@ -90,6 +89,11 @@ export class DefaultConfigurationService {

private registerPropertyTypes(): void {
uniq(flatten(this.propertyTypes)).forEach(propertyType => {
if (this.registeredObjects.has(propertyType)) {
return;
}
this.registeredObjects.add(propertyType);

if (typeof propertyType === 'object') {
this.propertyTypeLibrary.registerPropertyType(propertyType);
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Injectable } from '@angular/core';
import { BeforeModelDestroyedEvent } from '@hypertrace/hyperdash';
import { DashboardEventManagerService } from './dashboard-event-manager.service';

/**
* Injectable implementation of `BeforeModelDestroyedEvent`
*/
@Injectable({ providedIn: 'root' })
export class BeforeModelDestroyedEventService extends BeforeModelDestroyedEvent {
public constructor(dashboardEventManager: DashboardEventManagerService) {
super(dashboardEventManager);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { TestBed } from '@angular/core/testing';
import {
ArrayDeserializer,
ArraySerializer,
BeforeModelDestroyedEvent,
BOOLEAN_PROPERTY,
DashboardEventManager,
DashboardManager,
Expand Down Expand Up @@ -42,6 +43,7 @@ import {
VariableSerializer
} from '@hypertrace/hyperdash';
import { ModelLibraryService } from '../injectable-wrappers/model-library.service';
import { BeforeModelDestroyedEventService } from './before-model-destroyed-event.service';
import { DashboardEventManagerService } from './dashboard-event-manager.service';
import { DashboardManagerService } from './dashboard-manager.service';
import { DataRefreshEventService } from './data-refresh-event.service';
Expand Down Expand Up @@ -112,6 +114,7 @@ describe('Injectable wrappers', () => {
expect(TestBed.inject(DataRefreshEventService) instanceof DataRefreshEvent).toBeTruthy();
expect(TestBed.inject(TimeRangeChangedEventService) instanceof TimeRangeChangedEvent).toBeTruthy();
expect(TestBed.inject(TimeRangeManagerService) instanceof TimeRangeManager).toBeTruthy();
expect(TestBed.inject(BeforeModelDestroyedEventService) instanceof BeforeModelDestroyedEvent).toBeTruthy();
});

test('support model decorators', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Injectable } from '@angular/core';
import { ModelManager } from '@hypertrace/hyperdash';
import { BeforeModelDestroyedEventService } from './before-model-destroyed-event.service';
import { LoggerService } from './logger.service';
import { ModelCreatedEventService } from './model-created-event.service';
import { ModelDestroyedEventService } from './model-destroyed-event.service';
Expand All @@ -12,8 +13,9 @@ export class ModelManagerService extends ModelManager {
public constructor(
logger: LoggerService,
modelDestroyedEvent: ModelDestroyedEventService,
modelCreatedEvent: ModelCreatedEventService
modelCreatedEvent: ModelCreatedEventService,
beforeModelDestroyedEvent: BeforeModelDestroyedEventService
) {
super(logger, modelCreatedEvent, modelDestroyedEvent);
super(logger, modelCreatedEvent, modelDestroyedEvent, beforeModelDestroyedEvent);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Injectable } from '@angular/core';
import { VariableManager } from '@hypertrace/hyperdash';
import { BeforeModelDestroyedEventService } from './before-model-destroyed-event.service';
import { LoggerService } from './logger.service';
import { ModelChangedEventService } from './model-changed-event.service';
import { ModelManagerService } from './model-manager.service';
Expand All @@ -12,8 +13,9 @@ export class VariableManagerService extends VariableManager {
public constructor(
logger: LoggerService,
modelManager: ModelManagerService,
modelChangedEvent: ModelChangedEventService
modelChangedEvent: ModelChangedEventService,
beforeModelDestroyedEvent: BeforeModelDestroyedEventService
) {
super(logger, modelManager, modelChangedEvent);
super(logger, modelManager, modelChangedEvent, beforeModelDestroyedEvent);
}
}
Loading