Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use POJsOs to model Cluster. Same pattern as DataCube type. #886

Merged
merged 3 commits into from
Mar 17, 2022
Merged
Show file tree
Hide file tree
Changes from 2 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
21 changes: 21 additions & 0 deletions src/client/deserializers/cluster.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright 2017-2022 Allegro.pl
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { ClientCluster, SerializedCluster } from "../../common/models/cluster/cluster";

export function deserialize(cluster: SerializedCluster): ClientCluster {
return cluster;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is just for consistency - all models have serializers and deserializers?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More important is that every model has client and serialised version. Something that lives in browser and something that can be transferred via wire. Here by happy coincidence, client object is build from JSON primitives so it can be easily deserialised. But some time in the future we may have non primitive fields and deserialisation will be more complicated than identity function.

}
4 changes: 2 additions & 2 deletions src/client/deserializers/sources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@
*/

import { ClientAppSettings } from "../../common/models/app-settings/app-settings";
import { Cluster } from "../../common/models/cluster/cluster";
import { SerializedDataCube } from "../../common/models/data-cube/data-cube";
import { ClientSources, SerializedSources } from "../../common/models/sources/sources";
import { Ajax } from "../utils/ajax/ajax";
import { deserialize as clusterDeserialize } from "./cluster";
import { deserialize as dataCubeDeserialize } from "./data-cube";

export function deserialize(settings: SerializedSources, appSettings: ClientAppSettings): ClientSources {
const clusters = settings.clusters.map(cluster => Cluster.fromJS(cluster));
const clusters = settings.clusters.map(clusterDeserialize);

const dataCubes = settings.dataCubes.map((dataCube: SerializedDataCube) => {
const executor = Ajax.queryUrlExecutorFactory(dataCube.name, appSettings);
Expand Down
8 changes: 4 additions & 4 deletions src/common/models/cluster/cluster.fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import { Cluster, ClusterJS } from "./cluster";
import { Cluster, ClusterJS, fromConfig } from "./cluster";

export class ClusterFixtures {
static druidWikiClusterJS(): ClusterJS {
Expand All @@ -33,7 +33,7 @@ export class ClusterFixtures {
}

static druidWikiCluster(): Cluster {
return Cluster.fromJS(ClusterFixtures.druidWikiClusterJS());
return fromConfig(ClusterFixtures.druidWikiClusterJS());
}

static druidTwitterClusterJS(): ClusterJS {
Expand All @@ -52,11 +52,11 @@ export class ClusterFixtures {
}

static druidTwitterCluster(): Cluster {
return Cluster.fromJS(ClusterFixtures.druidTwitterClusterJS());
return fromConfig(ClusterFixtures.druidTwitterClusterJS());
}

static druidTwitterClusterJSWithGuard(guardDataCubes = true): Cluster {
return Cluster.fromJS({
return fromConfig({
name: "druid-custom",
url: "http://192.168.99.101",
version: "0.9.1",
Expand Down
168 changes: 126 additions & 42 deletions src/common/models/cluster/cluster.mocha.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,56 +15,140 @@
* limitations under the License.
*/

import { expect } from "chai";
import { testImmutableClass } from "immutable-class-tester";
import { Cluster, ClusterJS } from "./cluster";
import { expect, use } from "chai";
import equivalent from "../../../client/utils/test-utils/equivalent";
import { RequestDecorator } from "../../../server/utils/request-decorator/request-decorator";
import { RetryOptions } from "../../../server/utils/retry-options/retry-options";
import { ClusterJS, fromConfig } from "./cluster";

use(equivalent);

describe("Cluster", () => {
// TODO: reimplement this test as simpler cases without immutable-class-tester - it checks too much
it.skip("is an immutable class", () => {
testImmutableClass(Cluster, [
{
name: "my-druid-cluster"
},
{
name: "my-druid-cluster",
url: "https://192.168.99.100",
version: "0.9.1",
timeout: 30000,
healthCheckTimeout: 50,
sourceListScan: "auto",
sourceListRefreshOnLoad: true,
sourceListRefreshInterval: 10000,
sourceReintrospectInterval: 10000,

introspectionStrategy: "segment-metadata-fallback"
},
{
name: "my-mysql-cluster",
url: "http://192.168.99.100",
timeout: 30000,
sourceListScan: "auto"
},
{
name: "my-mysql-cluster",
url: "https://192.168.99.100",
timeout: 30000,
sourceListScan: "auto",
describe("fromConfig", () => {
it("should load defaults", () => {
const cluster = fromConfig({
name: "foobar",
url: "http://bazz"
});

expect(cluster).to.be.deep.equal({
name: "foobar",
guardDataCubes: false,
healthCheckTimeout: 1000,
introspectionStrategy: "segment-metadata-fallback",
requestDecorator: null,
retry: new RetryOptions(),
sourceListRefreshInterval: 0,
sourceReintrospectInterval: 0
}
]);
});
sourceListRefreshOnLoad: false,
sourceListScan: "auto",
sourceReintrospectInterval: 0,
sourceReintrospectOnLoad: false,
timeout: undefined,
title: "",
type: "druid",
url: "http://bazz",
version: null
});
});

it("should throw with incorrect name type", () => {
expect(() => fromConfig({ name: 1 } as unknown as ClusterJS)).to.throw("must be a string");
});

it("should throw with incorrect empty name", () => {
expect(() => fromConfig({ name: "", url: "http://foobar" })).to.throw("empty name");
});

it("should throw with not url safe name", () => {
expect(() => fromConfig({ name: "foobar%bazz#", url: "http://foobar" })).to.throw("is not a URL safe name");
});

it("should throw with name equal to native", () => {
expect(() => fromConfig({ name: "native", url: "http://foobar" })).to.throw("name can not be 'native'");
});

it("should read retry options", () => {
const cluster = fromConfig({
name: "foobar",
url: "http://foobar",
retry: {
maxAttempts: 1,
delay: 42
}
});

expect(cluster.retry).to.be.equivalent(new RetryOptions({ maxAttempts: 1, delay: 42 }));
});

it("should read request decorator", () => {
const cluster = fromConfig({
name: "foobar",
url: "http://foobar",
requestDecorator: {
path: "foobar",
options: { bazz: true }
}
});

expect(cluster.requestDecorator).to.be.equivalent(new RequestDecorator("foobar", { bazz: true }));
});

it("should read request decorator old format", () => {
const cluster = fromConfig({
name: "foobar",
url: "http://foobar",
requestDecorator: "foobar",
decoratorOptions: { bazz: true }
} as unknown as ClusterJS);

expect(cluster.requestDecorator).to.be.equivalent(new RequestDecorator("foobar", { bazz: true }));
});

describe("backward compatibility", () => {
it("should read old host and assume http protocol", () => {
const cluster = Cluster.fromJS({
const cluster = fromConfig({
name: "old-host",
host: "broker-host.com"
} as ClusterJS);
} as unknown as ClusterJS);

expect(cluster.url).to.be.eq("http://broker-host.com");
});
});

});
it("should override default values", () => {
const cluster = fromConfig({
guardDataCubes: true,
healthCheckTimeout: 42,
introspectionStrategy: "introspection-introspection",
name: "cluster-name",
sourceListRefreshInterval: 1123,
sourceListRefreshOnLoad: true,
sourceListScan: "auto",
sourceReintrospectInterval: 1432,
sourceReintrospectOnLoad: true,
timeout: 581,
title: "foobar-title",
url: "http://url-bazz",
version: "new-version"
});

expect(cluster).to.be.deep.equal({
guardDataCubes: true,
healthCheckTimeout: 42,
introspectionStrategy: "introspection-introspection",
name: "cluster-name",
sourceListRefreshInterval: 1123,
sourceListRefreshOnLoad: true,
sourceListScan: "auto",
sourceReintrospectInterval: 1432,
sourceReintrospectOnLoad: true,
timeout: 581,
title: "foobar-title",
url: "http://url-bazz",
version: "new-version",
type: "druid",
requestDecorator: null,
retry: new RetryOptions()
});
});
});
})
;
Loading