Skip to content
This repository was archived by the owner on Nov 3, 2022. It is now read-only.

Commit

Permalink
Merge pull request #782 from dedica-team/656_display_group_icon
Browse files Browse the repository at this point in the history
656 display group icon
  • Loading branch information
bonndan authored Nov 18, 2021
2 parents a8c5b67 + bc5651a commit 239c1e5
Show file tree
Hide file tree
Showing 13 changed files with 89 additions and 48 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IGroup } from '../../../../interfaces';
import {IGroup} from '../../../../interfaces';
import React from 'react';
import Avatar from '@material-ui/core/Avatar';
import componentStyles from '../../../../Resources/styling/ComponentStyles';
Expand Down Expand Up @@ -30,8 +30,9 @@ const GroupAvatar: React.FC<Props> = ({ group, statusColor }) => {
title={'Click to highlight the group.'}
style={{
backgroundColor: '#' + group.color,
paddingTop: 6,
paddingLeft: 1,
}}
src={group.icon}
>
{group.identifier[0].toUpperCase()}
</Avatar>
Expand Down
6 changes: 3 additions & 3 deletions src/main/app/src/Components/Landscape/Utils/utils.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { ReactElement } from 'react';
import { IGroup, IItem, ILandscape, IRelation } from '../../../interfaces';
import { Button, Link, List, ListItem, ListItemText } from '@material-ui/core';
import React, {ReactElement} from 'react';
import {IGroup, IItem, ILandscape, IRelation} from '../../../interfaces';
import {Button, Link, List, ListItem, ListItemText} from '@material-ui/core';
import MappedString from './MappedString';

/**
Expand Down
1 change: 1 addition & 0 deletions src/main/app/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export interface IGroup {
labels?: ILabels;
_links?: ILinks;
items: IItem[];
icon: string;
}

export interface IItem {
Expand Down
23 changes: 1 addition & 22 deletions src/main/app/src/labels.json
Original file line number Diff line number Diff line change
@@ -1,22 +1 @@
{
"capability": "The capability the service provides for the business or, in case of infrastructure, the technical capability like enabling service discovery, configuration, secrets, or persistence.",
"color": "A hex color code (items inherit group colors as default)",
"costs": "Running costs of the item.",
"fill": "Background image (for displaying purposes).",
"frameworks": "A comma-separated list of frameworks as key-value pairs (key is name, value is version).",
"health": "Description of the item's health status.",
"icon": "Icon/image (for displaying purposes).",
"label": "A custom label (like a note, but very short).",
"layer": "A technical layer.",
"lifecycle": "A lifecycle phase (``PLANNED|plan``, ``INTEGRATION|int``, ``PRODUCTION|prod``, ``END_OF_LIFE|eol|end``).",
"note": "A custom note.",
"scale": "Number of instances.",
"security": "Description of the item's security status.",
"shortname": "Abbreviated name.",
"software": "Software/OS name.",
"stability": "Description of the item's stability.",
"team": "Name of the responsible team (e.g. technical owner).",
"version": "The version (e.g. software version or protocol version).",
"visibility": "Visibility to other items.",
"weight": "Importance or relations. Used as factor for drawn width if numbers between 0 and 5 are given."
}
{"capability":"The capability the service provides for the business or, in case of infrastructure, the technical capability like enabling service discovery, configuration, secrets, or persistence.","color":"A hex color code (items inherit group colors as default)","costs":"Running costs of the item.","fill":"Background image (for displaying purposes).","frameworks":"A comma-separated list of frameworks as key-value pairs (key is name, value is version).","health":"Description of the item's health status.","icon":"Icon/image (for displaying purposes).","label":"A custom label (like a note, but very short).","layer":"A technical layer.","lifecycle":"A lifecycle phase (``PLANNED|plan``, ``INTEGRATION|int``, ``PRODUCTION|prod``, ``END_OF_LIFE|eol|end``).","note":"A custom note.","scale":"Number of instances.","security":"Description of the item's security status.","shortname":"Abbreviated name.","software":"Software/OS name.","stability":"Description of the item's stability.","team":"Name of the responsible team (e.g. technical owner).","version":"The version (e.g. software version or protocol version).","visibility":"Visibility to other items.","weight":"Importance or relations. Used as factor for drawn width if numbers between 0 and 5 are given."}
1 change: 1 addition & 0 deletions src/main/app/src/utils/testing/LandscapeContextValue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const groups: IGroup[] = [
name: 'A Group',
items: items,
identifier: 'groupA',
icon: '',
},
];

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package de.bonndan.nivio.output.icons;

public class IconCannotBeLoadedException extends RuntimeException {

public IconCannotBeLoadedException(String errorMessage) {
super(errorMessage);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class IconMapping {
private static final Logger LOGGER = LoggerFactory.getLogger(IconMapping.class);

public static final String DEFAULT_ICON = "cog";
public static final String DEFAULT_GROUP_ICON = "hexagon-multiple-outline";

public static final String BACKEND = "backend";
public static final String CACHE = "cache";
Expand Down
4 changes: 1 addition & 3 deletions src/main/java/de/bonndan/nivio/output/icons/IconService.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@

/**
* Provides the builtin icons (shipped with nivio) and vendor icons (loaded form remote locations) as embeddable data.
*
*
*/
@Service
public class IconService {
Expand Down Expand Up @@ -53,7 +51,7 @@ public String getIconUrl(Item item) {
}

Optional<String> iconUrl = localIcons.getIconUrl(icon);
if(iconUrl.isPresent()) {
if (iconUrl.isPresent()) {
if (iconUrl.get().startsWith("http")) {
try {
return externalIcons.getUrl(new URL(iconUrl.get())).orElse(iconUrl.get());
Expand Down
16 changes: 14 additions & 2 deletions src/main/java/de/bonndan/nivio/output/icons/LocalIcons.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

import static de.bonndan.nivio.output.icons.IconMapping.DEFAULT_GROUP_ICON;
import static de.bonndan.nivio.output.icons.IconMapping.DEFAULT_ICON;

/**
Expand All @@ -21,7 +22,7 @@
public class LocalIcons {

private static final Logger LOGGER = LoggerFactory.getLogger(LocalIcons.class);
private static final String initErrorMsg = "Default icon could not be loaded from icon set folder %s." +
private static final String INIT_ERROR_MSG = "Default icon could not be loaded from icon set folder %s." +
" Make sure all npm dependencies are installed (or run mvn package).";
public static final String DEFAULT_ICONS_FOLDER = "/static/icons/svg/";

Expand All @@ -30,6 +31,8 @@ public class LocalIcons {
*/
private final String defaultIcon;

private final String defaultGroupIcon;

private final String iconFolder;

/**
Expand All @@ -49,8 +52,13 @@ public LocalIcons(@NonNull final String iconFolder) {
this.iconFolder = DEFAULT_ICONS_FOLDER;
}
defaultIcon = getIconUrl(DEFAULT_ICON).orElseThrow(() -> {
throw new RuntimeException(String.format(initErrorMsg, this.iconFolder));
throw new IconCannotBeLoadedException(String.format(INIT_ERROR_MSG, this.iconFolder));
});

defaultGroupIcon = getIconUrl(DEFAULT_GROUP_ICON).orElseThrow(() -> {
throw new IconCannotBeLoadedException(String.format(INIT_ERROR_MSG, this.iconFolder));
});

}

public LocalIcons() {
Expand Down Expand Up @@ -86,6 +94,10 @@ public String getDefaultIcon() {
return defaultIcon;
}

public String getDefaultGroupIcon() {
return defaultGroupIcon;
}

/**
* Creates a SVG data url from the given resource path. Is cached.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package de.bonndan.nivio.output.layout;

import de.bonndan.nivio.model.Item;
import de.bonndan.nivio.model.Label;
import de.bonndan.nivio.model.Labeled;
import de.bonndan.nivio.model.Landscape;
import de.bonndan.nivio.model.*;
import de.bonndan.nivio.output.icons.IconService;
import de.bonndan.nivio.output.icons.LocalIcons;
import de.bonndan.nivio.util.URLHelper;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
Expand All @@ -26,13 +24,13 @@ public AppearanceProcessor(IconService iconService) {

public void process(@NonNull final Landscape landscape) {
Objects.requireNonNull(landscape).getGroupItems().forEach(group -> {
setIconFillAppearance(group);
landscape.getItems().retrieve(group.getItems()).forEach(this::setIconFillAppearance);
setIconAndFillAppearance(group);
landscape.getItems().retrieve(group.getItems()).forEach(this::setIconAndFillAppearance);
});
setIconFillAppearance(landscape);
setIconAndFillAppearance(landscape);
}

private void setIconFillAppearance(Labeled labeled) {
private void setIconAndFillAppearance(Labeled labeled) {

if (labeled instanceof Item) {
labeled.setLabel(Label._icondata, iconService.getIconUrl((Item) labeled));
Expand All @@ -43,6 +41,10 @@ private void setIconFillAppearance(Labeled labeled) {
.flatMap(iconService::getExternalUrl)
.ifPresent(s -> labeled.setLabel(Label._icondata, s));
}
if (!StringUtils.hasLength(icon) && labeled instanceof Group) {
LocalIcons localIcons = new LocalIcons();
labeled.setLabel(Label._icondata, localIcons.getDefaultGroupIcon());
}
}

String fill = labeled.getLabel(Label.fill);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package de.bonndan.nivio.util;

public class IconCannotBeLoadedException extends RuntimeException {

public IconCannotBeLoadedException(String errorMessage) {
super(errorMessage);
}
}
15 changes: 15 additions & 0 deletions src/test/java/de/bonndan/nivio/output/icons/LocalIconsTest.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package de.bonndan.nivio.output.icons;

import org.junit.Assert;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.util.Base64;
import java.util.Optional;

import static de.bonndan.nivio.output.icons.IconMapping.DEFAULT_GROUP_ICON;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.when;

class LocalIconsTest {

Expand Down Expand Up @@ -47,4 +51,15 @@ void returnsDefault() {
void returnsTypeIgnoreCase() {
assertThat(localIcons.getIconUrl("AccOunT")).isNotEmpty();
}

@Test
void returnsGroupDefault() {
String icon = localIcons.getDefaultGroupIcon();
assertThat(icon).isNotEmpty();

String payload = icon.replace(DataUrlHelper.DATA_IMAGE_SVG_XML_BASE_64, "");
String decoded = new String(Base64.getDecoder().decode(payload));
assertThat(decoded).contains("xml");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,18 @@
import de.bonndan.nivio.model.*;
import de.bonndan.nivio.output.icons.DataUrlHelper;
import de.bonndan.nivio.output.icons.IconService;
import de.bonndan.nivio.output.icons.LocalIcons;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

import static de.bonndan.nivio.model.ItemFactory.getTestItem;
import static de.bonndan.nivio.model.ItemFactory.getTestItemBuilder;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

Expand All @@ -24,13 +23,15 @@ class AppearanceProcessorTest {
private AppearanceProcessor resolver;
private Landscape landscape;
private IconService iconService;
private LocalIcons localIcons;
private Group g1;
private ArrayList<Item> items;

@BeforeEach
public void setup() {

iconService = mock(IconService.class);
localIcons = mock(LocalIcons.class);
landscape = LandscapeFactory.createForTesting("l1", "l1Landscape").build();
resolver = new AppearanceProcessor(iconService);

Expand All @@ -54,7 +55,7 @@ public void setup() {
}

@Test
void setItemIcons_LabelIcon() {
void item_icon_setIconAndFillAppearance() {
Item s1 = landscape.getItems().pick("s1", "g1");
s1.setLabel(Label.icon, "https://dedica.team/images/logo_orange_weiss.png");
when(iconService.getIconUrl(s1)).thenReturn(DataUrlHelper.DATA_IMAGE_SVG_XML_BASE_64 + "foo");
Expand All @@ -67,7 +68,7 @@ void setItemIcons_LabelIcon() {
}

@Test
void setItemIcons_LabelFill() throws MalformedURLException {
void item_fill_setIconAndFillAppearance() throws MalformedURLException {
Item s1 = landscape.getItems().pick("s1", "g1");
s1.setLabel(Label.fill, "http://dedica.team/images/portrait.jpeg");
when(iconService.getExternalUrl(new URL(s1.getLabel(Label.fill)))).thenReturn(java.util.Optional.of(DataUrlHelper.DATA_IMAGE_SVG_XML_BASE_64 + "foo"));
Expand All @@ -80,7 +81,7 @@ void setItemIcons_LabelFill() throws MalformedURLException {
}

@Test
void setLandscapeIcons_LabelIcon() throws MalformedURLException {
void landscape_icon_setIconAndFillAppearance() throws MalformedURLException {

// given
landscape.setLabel(Label.icon, "https://dedica.team/images/logo_orange_weiss.png");
Expand All @@ -94,7 +95,7 @@ void setLandscapeIcons_LabelIcon() throws MalformedURLException {
}

@Test
void setLandscapeIcons_LabelFill() throws MalformedURLException {
void landscape_fill_setIconAndFillAppearance() throws MalformedURLException {

// given
landscape.setLabel(Label.fill, "http://dedica.team/images/portrait.jpeg");
Expand All @@ -108,7 +109,7 @@ void setLandscapeIcons_LabelFill() throws MalformedURLException {
}

@Test
void setGroupIcons_LabelIcon() throws MalformedURLException {
void group_icon_setIconAndFillAppearance() throws MalformedURLException {

// given
g1.setLabel(Label.icon, "https://dedica.team/images/logo_orange_weiss.png");
Expand All @@ -122,7 +123,7 @@ void setGroupIcons_LabelIcon() throws MalformedURLException {
}

@Test
void setGroupIcons_LabelFill() throws MalformedURLException {
void group_fill_setIconAndFillAppearance() throws MalformedURLException {

// given
g1.setLabel(Label.fill, "http://dedica.team/images/portrait.jpeg");
Expand All @@ -135,4 +136,18 @@ void setGroupIcons_LabelFill() throws MalformedURLException {
assertThat(g1.getLabel(Label._filldata)).isEqualTo(DataUrlHelper.DATA_IMAGE_SVG_XML_BASE_64 + "foo");
}

@Test
void group_setDefaultIcon_setIconAndFillAppearance() {

// given
// default group icon
when(localIcons.getDefaultGroupIcon()).thenReturn(DataUrlHelper.DATA_IMAGE_SVG_XML_BASE_64 + "PD94bWwg");

// when
resolver.process(landscape);

// then
assertThat(g1.getIcon()).startsWith(DataUrlHelper.DATA_IMAGE_SVG_XML_BASE_64 + "PD94bWwg");
}

}

0 comments on commit 239c1e5

Please sign in to comment.