Skip to content

Commit

Permalink
Merge pull request #74 from ScottLogic/VUU-27-remote-layout-management
Browse files Browse the repository at this point in the history
VUU-27 remotePersistenceManager implementation
  • Loading branch information
vferraro-scottlogic authored Oct 25, 2023
2 parents 340d517 + 1935ee9 commit 679155b
Show file tree
Hide file tree
Showing 15 changed files with 651 additions and 135 deletions.
1 change: 1 addition & 0 deletions .semgrepignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ vuu/src/main/resources/www/ws-example.html
vuu/src/main/scala/org/finos/vuu/provider/simulation/SimulatedBigInstrumentsProvider.scala
vuu-ui/packages/vuu-data/src/array-data-source/group-utils.ts
vuu-ui/packages/vuu-datagrid-extras/src/column-expression-input/column-language-parser/walkExpressionTree.ts
vuu-ui/packages/vuu-layout/src/layout-persistence/RemoteLayoutPersistenceManager.ts
vuu-ui/packages/vuu-popups/src/menu/useContextMenu.tsx
vuu-ui/packages/vuu-table-extras/src/cell-edit-validators/PatternValidator.ts
vuu-ui/packages/vuu-ui-controls/src/list/Highlighter.tsx
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.finos.vuu.layoutserver;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://127.0.0.1:5173")
.allowedMethods("GET", "POST", "PUT", "DELETE");
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { LayoutJSON } from "@finos/vuu-layout";
import { LayoutMetadata } from "@finos/vuu-shell";
import { LayoutMetadata, LayoutMetadataDto } from "@finos/vuu-shell";

export interface LayoutPersistenceManager {
/**
Expand All @@ -10,7 +10,7 @@ export interface LayoutPersistenceManager {
*
* @returns Unique identifier assigned to the saved layout
*/
createLayout: (metadata: Omit<LayoutMetadata, "id">, layout: LayoutJSON) => Promise<string>;
createLayout: (metadata: LayoutMetadataDto, layout: LayoutJSON) => Promise<LayoutMetadata>;

/**
* Overwrites an existing layout and its corresponding metadata with the provided information
Expand All @@ -19,7 +19,7 @@ export interface LayoutPersistenceManager {
* @param metadata - Metadata describing the new layout to overwrite with
* @param layout - Full JSON representation of the new layout to overwrite with
*/
updateLayout: (id: string, metadata: Omit<LayoutMetadata, "id">, layout: LayoutJSON) => Promise<void>;
updateLayout: (id: string, metadata: LayoutMetadataDto, layout: LayoutJSON) => Promise<void>;

/**
* Deletes an existing layout and its corresponding metadata
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
import { Layout, LayoutMetadata, WithId } from "@finos/vuu-shell";
import { LayoutJSON, LayoutPersistenceManager } from "@finos/vuu-layout";
import { getLocalEntity, saveLocalEntity } from "@finos/vuu-filters";
import { getUniqueId } from "@finos/vuu-utils";
import {
Layout,
LayoutMetadata,
LayoutMetadataDto,
WithId,
} from "@finos/vuu-shell";
import { formatDate, getUniqueId } from "@finos/vuu-utils";

import { defaultLayout } from "./data";
import { LayoutPersistenceManager } from "./LayoutPersistenceManager";
import { LayoutJSON } from "../layout-reducer";
import { getLocalEntity, saveLocalEntity } from "@finos/vuu-filters";

const metadataSaveLocation = "layouts/metadata";
const layoutsSaveLocation = "layouts/layouts";

export class LocalLayoutPersistenceManager implements LayoutPersistenceManager {
createLayout(
metadata: Omit<LayoutMetadata, "id">,
metadata: LayoutMetadataDto,
layout: LayoutJSON
): Promise<string> {
): Promise<LayoutMetadata> {
return new Promise((resolve) => {
console.log(
`Saving layout as ${metadata.name} to group ${metadata.group}...`
Expand All @@ -21,33 +27,38 @@ export class LocalLayoutPersistenceManager implements LayoutPersistenceManager {
Promise.all([this.loadLayouts(), this.loadMetadata()]).then(
([existingLayouts, existingMetadata]) => {
const id = getUniqueId();
this.appendAndPersist(
const newMetadata: LayoutMetadata = {
...metadata,
id,
metadata,
layout,
existingLayouts,
existingMetadata
created: formatDate(new Date(), "dd.mm.yyyy"),
};

this.saveLayoutsWithMetadata(
[...existingLayouts, { id, json: layout }],
[...existingMetadata, newMetadata]
);
resolve(id);
resolve(newMetadata);
}
);
});
}

updateLayout(
id: string,
newMetadata: Omit<LayoutMetadata, "id">,
newMetadata: LayoutMetadataDto,
newLayout: LayoutJSON
): Promise<void> {
return new Promise((resolve, reject) => {
this.validateIds(id)
.then(() => Promise.all([this.loadLayouts(), this.loadMetadata()]))
.then(([existingLayouts, existingMetadata]) => {
const layouts = existingLayouts.filter((layout) => layout.id !== id);
const metadata = existingMetadata.filter(
(metadata) => metadata.id !== id
const updatedLayouts = existingLayouts.map((layout) =>
layout.id === id ? { ...layout, json: newLayout } : layout
);
this.appendAndPersist(id, newMetadata, newLayout, layouts, metadata);
const updatedMetadata = existingMetadata.map((metadata) =>
metadata.id === id ? { ...metadata, ...newMetadata } : metadata
);
this.saveLayoutsWithMetadata(updatedLayouts, updatedMetadata);
resolve();
})
.catch((e) => reject(e));
Expand Down Expand Up @@ -75,10 +86,14 @@ export class LocalLayoutPersistenceManager implements LayoutPersistenceManager {
this.validateId(id, "layout")
.then(() => this.loadLayouts())
.then((existingLayouts) => {
const layouts = existingLayouts.find(
const foundLayout = existingLayouts.find(
(layout) => layout.id === id
) as Layout;
resolve(layouts.json);
);
if (foundLayout) {
resolve(foundLayout.json);
} else {
reject(new Error(`no layout found matching id ${id}`));
}
})
.catch((e) => reject(e));
});
Expand Down Expand Up @@ -123,19 +138,6 @@ export class LocalLayoutPersistenceManager implements LayoutPersistenceManager {
});
}

private appendAndPersist(
newId: string,
newMetadata: Omit<LayoutMetadata, "id">,
newLayout: LayoutJSON,
existingLayouts: Layout[],
existingMetadata: LayoutMetadata[]
) {
existingLayouts.push({ id: newId, json: newLayout });
existingMetadata.push({ id: newId, ...newMetadata });

this.saveLayoutsWithMetadata(existingLayouts, existingMetadata);
}

private saveLayoutsWithMetadata(
layouts: Layout[],
metadata: LayoutMetadata[]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { LayoutMetadata, LayoutMetadataDto } from "@finos/vuu-shell";
import { LayoutPersistenceManager } from "./LayoutPersistenceManager";
import { LayoutJSON } from "../layout-reducer";
import { defaultLayout } from "./data";

const DEFAULT_SERVER_BASE_URL = "http://127.0.0.1:8081/api";

const baseURL = process.env.LAYOUT_BASE_URL ?? DEFAULT_SERVER_BASE_URL;
const metadataSaveLocation = "layouts/metadata";
const layoutsSaveLocation = "layouts";

export type CreateLayoutResponseDto = { metadata: LayoutMetadata };
export type GetLayoutResponseDto = { definition: LayoutJSON };

export class RemoteLayoutPersistenceManager
implements LayoutPersistenceManager
{
createLayout(
metadata: LayoutMetadataDto,
layout: LayoutJSON
): Promise<LayoutMetadata> {
return new Promise((resolve, reject) =>
fetch(`${baseURL}/${layoutsSaveLocation}`, {
headers: {
"Content-Type": "application/json",
},
method: "POST",
body: JSON.stringify({
metadata,
definition: JSON.stringify(layout),
}),
})
.then((response) => {
if (!response.ok) {
reject(new Error(response.statusText));
}
response.json().then(({ metadata }: CreateLayoutResponseDto) => {
if (!metadata) {
reject(new Error("Response did not contain valid metadata"));
}
resolve(metadata);
});
})
.catch((error: Error) => {
reject(error);
})
);
}

updateLayout(
id: string,
metadata: LayoutMetadataDto,
newLayoutJson: LayoutJSON
): Promise<void> {
return new Promise((resolve, reject) =>
fetch(`${baseURL}/${layoutsSaveLocation}/${id}`, {
method: "PUT",
body: JSON.stringify({
metadata,
layout: newLayoutJson,
}),
})
.then((response) => {
if (!response.ok) {
reject(new Error(response.statusText));
}
resolve();
})
.catch((error: Error) => {
reject(error);
})
);
}

deleteLayout(id: string): Promise<void> {
return new Promise((resolve, reject) =>
fetch(`${baseURL}/${layoutsSaveLocation}/${id}`, {
method: "DELETE",
})
.then((response) => {
if (!response.ok) {
reject(new Error(response.statusText));
}
resolve();
})
.catch((error: Error) => {
reject(error);
})
);
}

loadLayout(id: string): Promise<LayoutJSON> {
return new Promise((resolve, reject) => {
fetch(`${baseURL}/${layoutsSaveLocation}/${id}`, {
method: "GET",
})
.then((response) => {
if (!response.ok) {
reject(new Error(response.statusText));
}
response.json().then(({ definition }: GetLayoutResponseDto) => {
if (!definition) {
reject(new Error("Response did not contain a valid layout"));
}
resolve(definition);
});
})
.catch((error: Error) => {
reject(error);
});
});
}

loadMetadata(): Promise<LayoutMetadata[]> {
return new Promise((resolve, reject) =>
fetch(`${baseURL}/${metadataSaveLocation}`, {
method: "GET",
})
.then((response) => {
if (!response.ok) {
reject(new Error(response.statusText));
}
response.json().then((metadata: LayoutMetadata[]) => {
if (!metadata) {
reject(new Error("Response did not contain valid metadata"));
}
resolve(metadata);
});
})
.catch((error: Error) => {
reject(error);
})
);
}

saveApplicationLayout(layout: LayoutJSON): Promise<void> {
// TODO POST api/layouts/application #71
console.log(layout);
return new Promise((resolve) => resolve());
}

loadApplicationLayout(): Promise<LayoutJSON> {
// TODO GET api/layouts/application #71
return new Promise((resolve) => resolve(defaultLayout));
}
}
1 change: 1 addition & 0 deletions vuu-ui/packages/vuu-layout/src/layout-persistence/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from "./data";
export * from "./LayoutPersistenceManager";
export * from "./LocalLayoutPersistenceManager";
export * from './RemoteLayoutPersistenceManager';
export * from "./useLayoutContextMenuItems";
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
LayoutMetadata,
LayoutMetadataDto,
SaveLayoutPanel,
useLayoutManager,
} from "@finos/vuu-shell";
Expand All @@ -21,7 +21,7 @@ export const useLayoutContextMenuItems = () => {
}, []);

const handleSave = useCallback(
(layoutMetadata: Omit<LayoutMetadata, "id">) => {
(layoutMetadata: LayoutMetadataDto) => {
saveLayout(layoutMetadata);
setDialogContent(undefined);
},
Expand Down
Loading

0 comments on commit 679155b

Please sign in to comment.