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

Terminology api v2 #748

Draft
wants to merge 37 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
78f6e64
use api v2 for frontpage: terminology search, organizations, groups, …
kkyttala Aug 22, 2024
f60e613
terminology page, concept list, rename vocabulary -> terminology
kkyttala Aug 23, 2024
c1270ff
concept page api v2
kkyttala Aug 26, 2024
d46c301
concept collections view and remove concepts, collections and termino…
kkyttala Aug 27, 2024
aaeb244
import concepts NTRF
kkyttala Aug 27, 2024
9d1c044
collection add and edit
kkyttala Aug 28, 2024
5095d99
revert next config change
kkyttala Aug 28, 2024
e9921e8
collection form validation
kkyttala Aug 29, 2024
b5829c2
terminology add and edit
kkyttala Aug 29, 2024
dc7efe5
create and edit concept
kkyttala Sep 3, 2024
57f1e9f
refactor comparing locales, add translations
kkyttala Sep 3, 2024
a0ccbc3
add missing search functionalities, fix enum values, handle resource …
kkyttala Sep 5, 2024
c45e3a2
fix lint error
kkyttala Sep 5, 2024
661382c
fix build errors and translations. Fix collection payload generation
kkyttala Sep 5, 2024
a430b43
fix and rewrite tests
kkyttala Sep 6, 2024
d1839e7
remove unused code
kkyttala Sep 6, 2024
45f788c
v1 redirectinos, remove search terms from public view, code clean up
kkyttala Sep 10, 2024
a90a45e
fix redirect config
kkyttala Sep 10, 2024
f453fff
fix sitemap generation
kkyttala Sep 10, 2024
4c21cc8
remove automatic prefix functionality
kkyttala Sep 11, 2024
065c7f1
revert next config change and fix typo
kkyttala Sep 12, 2024
778258e
use new MultiLingual component in datamodel-ui
kkyttala Sep 12, 2024
a4cf2d6
fix prefix validation and remove unused translations
kkyttala Sep 13, 2024
d73a71f
remove debug, commented and unused code
kkyttala Sep 13, 2024
21506f2
fix term family translation
kkyttala Oct 9, 2024
7f01978
fix build error
kkyttala Nov 4, 2024
6adcc89
fix error handling in excel import
kkyttala Oct 29, 2024
8377041
fix search parameter prefLang to sortlang
kkyttala Nov 4, 2024
0f3c1de
fix conflicts
kkyttala Nov 12, 2024
19ccb95
rename categories to types in count response
kkyttala Nov 8, 2024
b44cac3
allow html tags in terminology description
kkyttala Nov 8, 2024
f33d19f
fix user requests api endpoint
kkyttala Nov 8, 2024
4cb463f
sort languages list
kkyttala Nov 12, 2024
dcb6014
Merge pull request #751 from VRK-YTI/terminology-4103-user-requests-a…
kkyttala Nov 12, 2024
01bdbb7
fix page titles
kkyttala Nov 13, 2024
354c238
add hos header to sitemap request
kkyttala Nov 14, 2024
66dfd01
Merge pull request #754 from VRK-YTI/terminology-yti-4256-fix-page-ti…
kkyttala Nov 15, 2024
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
2 changes: 2 additions & 0 deletions common-ui/components/block/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import BasicBlock from './basic-block';
import MultilingualBlock from './multilingual-block';
import MultilingualBlockList from './multilingual-block-list';
import {
BasicBlockWrapper,
BasicBlockExtraWrapper,
Expand All @@ -9,6 +10,7 @@ import {
export {
BasicBlock,
MultilingualBlock,
MultilingualBlockList,
BasicBlockWrapper,
BasicBlockExtraWrapper,
List,
Expand Down
41 changes: 41 additions & 0 deletions common-ui/components/block/multilingual-block-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {
MultilingualBlockItem,
MultilingualBlockWrapper,
} from './multilingual-block.styles';
import { v4 } from 'uuid';
import { maxBy } from 'lodash';
import SanitizedTextContent from '../../components/sanitized-text-content';

interface MultilingualBlockProps {
data?: {
language: string;
value: string;
}[];
renderHtml?: boolean;
}

export default function MultilingualBlockList({
data,
renderHtml,
}: MultilingualBlockProps) {
if (!data) {
return <></>;
}

const id = v4().slice(0, 8);
const maxSize =
maxBy(data, (item) => item.language.length)?.language.length ?? 0;

return (
<MultilingualBlockWrapper $maxSize={maxSize}>
{data.map((d, idx) => (
<MultilingualBlockItem
key={`multilingual-block-${id}-item-${idx}`}
lang={d.language}
>
{renderHtml ? <SanitizedTextContent text={d.value} /> : d.value}
</MultilingualBlockItem>
))}
</MultilingualBlockWrapper>
);
}
31 changes: 21 additions & 10 deletions common-ui/components/block/multilingual-block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,41 @@ import {
} from './multilingual-block.styles';
import { v4 } from 'uuid';
import { maxBy } from 'lodash';
import SanitizedTextContent from '../../components/sanitized-text-content';
import { compareLocales } from '../../utils/compare-locales';

interface MultilingualBlockProps {
data?: {
lang: string;
value: string | JSX.Element;
}[];
data: { [key: string]: string };
renderHtml?: boolean;
}

export default function MultilingualBlock({ data }: MultilingualBlockProps) {
export default function MultilingualBlock({
data,
renderHtml,
}: MultilingualBlockProps) {
if (!data) {
return <></>;
}

const id = v4().slice(0, 8);
const maxSize = maxBy(data, (item) => item.lang.length)?.lang.length ?? 0;
const maxSize = maxBy(Object.keys(data), (key) => key.length)?.length ?? 0;

const sortedData =
Object.keys(data)
?.slice()
.sort((t1, t2) => compareLocales(t1, t2))
.reduce((result, lang) => {
result[lang] = data[lang];
return result;
}, {} as { [key: string]: string }) ?? {};

return (
<MultilingualBlockWrapper $maxSize={maxSize}>
{data.map((d, idx) => (
{Object.keys(sortedData).map((key, idx) => (
<MultilingualBlockItem
key={`multilingual-block-${id}-item-${idx}`}
lang={d.lang}
lang={key}
>
{d.value}
{renderHtml ? <SanitizedTextContent text={data[key]} /> : data[key]}
</MultilingualBlockItem>
))}
</MultilingualBlockWrapper>
Expand Down
11 changes: 8 additions & 3 deletions common-ui/components/form/language-selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
DescriptionInput,
} from './language-selector.styles';
import { TEXT_AREA_MAX, TEXT_INPUT_MAX } from '../../utils/constants';
import { compareLocales } from '../../utils/compare-locales';

export interface LanguageBlockType {
labelText: string;
Expand Down Expand Up @@ -92,6 +93,10 @@ export default function LanguageSelector(
);
};

const sortedItems = selectedItems
.slice()
.sort((a, b) => compareLocales(a.uniqueItemId, b.uniqueItemId));

return (
<>
<MultiSelect
Expand All @@ -102,12 +107,12 @@ export default function LanguageSelector(
visualPlaceholder={props.visualPlaceholder}
$isWide={props.isWide}
allowItemAddition={false}
selectedItems={selectedItems}
selectedItems={sortedItems}
onItemSelectionsChange={(e) =>
handleSelectedChange(e as (MultiSelectData & LanguageBlockType)[])
}
onRemoveAll={() => setSelectedItems([])}
defaultSelectedItems={selectedItems}
defaultSelectedItems={sortedItems}
ariaChipActionLabel={props.ariaChipActionLabel ?? ''}
ariaSelectedAmountText={props.ariaSelectedAmountText ?? ''}
ariaOptionsAvailableText={props.ariaOptionsAvailableText ?? ''}
Expand All @@ -118,7 +123,7 @@ export default function LanguageSelector(
id="language-selector"
/>

{selectedItems.map((item, idx) => (
{sortedItems.map((item, idx) => (
<LanguageBlock
padding="m"
className="language-block"
Expand Down
2 changes: 1 addition & 1 deletion common-ui/components/form/prefix.styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { TextInput as TextInputDS } from 'suomifi-ui-components';

export const TextInput = styled(TextInputDS)`
width: 100%;
max-width: ${(props) => (props.fullWidth ? '100%' : '450px')};
max-width: ${(props) => (props.fullWidth ? '100%' : '480px')};
`;

export const PrefixContainer = styled.div`
Expand Down
7 changes: 3 additions & 4 deletions common-ui/components/form/prefix.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ export default function Prefix({
maxLength,
minLength,
}: PrefixProps) {
const URI =
typeInUri === 'model' ? 'https://iri.suomi.fi' : 'http://uri.suomi.fi';
const namespace = 'https://iri.suomi.fi';
const [prefixValid, setPrefixValid] = useState(true);
const [prefixInternal, setPrefixInternal] = useState(prefix);

Expand Down Expand Up @@ -98,8 +97,8 @@ export default function Prefix({
<Label>{translations.uriPreview}</Label>
<Paragraph>
<Text smallScreen>
{URI}/{typeInUri}/{prefixInternal}
{prefixInternal && '/'}
{namespace}/{typeInUri}/{prefix}
{prefix && '/'}
</Text>
</Paragraph>
</div>
Expand Down
5 changes: 5 additions & 0 deletions common-ui/interfaces/error.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface ApiError {
status: string;
message: string;
details?: string;
}
7 changes: 7 additions & 0 deletions common-ui/interfaces/group.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface Group {
id: string;
label: {
[key: string]: string;
};
identifier: string;
}
2 changes: 1 addition & 1 deletion common-ui/interfaces/organization.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ export interface Organization {
label: {
[key: string]: string;
};
parentId?: string;
parentOrganization?: string;
}
19 changes: 19 additions & 0 deletions common-ui/utils/compare-locales.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export function compareLocales(locale1: string, locale2: string): number {
if (locale1 === 'fi' || locale2 === 'fi') {
return locale1 === 'fi' ? -1 : 1;
}

if (locale1 === 'sv' || locale2 === 'sv') {
return locale1 === 'sv' ? -1 : 1;
}

if (locale1 === 'en' || locale2 === 'en') {
return locale1 === 'en' ? -1 : 1;
}

if (locale1 !== locale2) {
return locale1 > locale2 ? 1 : -1;
}

return 0;
}
32 changes: 32 additions & 0 deletions common-ui/utils/get-language-version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
interface getLanguageVersionProps {
data?: { [key: string]: string };
lang?: string;
appendLocale?: boolean;
}

export function getLanguageVersion({
data,
lang = 'fi',
appendLocale = false,
}: getLanguageVersionProps) {
if (!data) {
return '';
}

if (data[lang]) {
return data[lang];
}

if (data.fi) {
return appendLocale ? `${data.fi} (fi)` : data.fi;
} else if (data.en) {
return appendLocale ? `${data.en} (en)` : data.en;
}

if (Object.keys(data).length > 0) {
const localeKey = Object.keys(data)[0];
return appendLocale ? `${data[localeKey]} (${localeKey})` : data[localeKey];
}

return '';
}
15 changes: 2 additions & 13 deletions datamodel-ui/src/modules/model/model-info-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -295,22 +295,11 @@ export default function ModelInfoView({

<DrawerContent height={headerHeight}>
<BasicBlock title={t('name')}>
<MultilingualBlock
data={Object.entries(modelInfo.label)
.sort((a, b) => compareLocales(a[0], b[0]))
.map((l) => ({ lang: l[0], value: l[1] }))}
/>
<MultilingualBlock data={modelInfo.label} />
</BasicBlock>
<BasicBlock title={t('description')}>
{Object.keys(modelInfo.description).length > 0 ? (
<MultilingualBlock
data={Object.entries(modelInfo.description)
.sort((a, b) => compareLocales(a[0], b[0]))
.map((d) => ({
lang: d[0],
value: <SanitizedTextContent text={d[1]} />,
}))}
/>
<MultilingualBlock renderHtml={true} data={modelInfo.description} />
) : (
t('not-added')
)}
Expand Down
2 changes: 1 addition & 1 deletion terminology-ui/.env
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# curl -v http://yti-terminology-api:9103/terminology-api/actuator/info
# API url to be used by SSR
TERMINOLOGY_API_URL=http://yti-terminology-api:9103/terminology-api
TERMINOLOGY_API_URL=http://yti-terminology-api:9103/terminology-api/v2
MESSAGING_API_URL=http://yti-messaging-api:9801/messaging-api
AUTH_PROXY_URL=http://yti-auth-proxy

Expand Down
2 changes: 1 addition & 1 deletion terminology-ui/jest.setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as crypto from 'crypto';
import { TextEncoder, TextDecoder } from 'util';

process.env.TERMINOLOGY_API_URL =
'http://terminology-api.invalid/terminology-api';
'http://terminology-api.invalid/terminology-api/v2';
process.env.AUTH_PROXY_URL = 'http://auth-proxy.invalid';
process.env.SECRET_COOKIE_PASSWORD = crypto.randomBytes(16).toString('hex');

Expand Down
23 changes: 23 additions & 0 deletions terminology-ui/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ module.exports = (phase, { defaultConfig }) => {
},
};

const uuidRegexp =
'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}';

if (process.env.REWRITE_PROFILE === 'local') {
// if run locally in a development environment, the API may run either as a
// container or as a process, but will always listen on the same port
Expand All @@ -107,6 +110,16 @@ module.exports = (phase, { defaultConfig }) => {
// https://nextjs.org/docs/api-reference/next.config.js/rewrites
async rewrites() {
return [
{
source: `/terminology/:terminologyId(${uuidRegexp})`,
destination:
'http://localhost:9103/terminology-api/v2/resolve/v1?termedId=:terminologyId',
},
{
source: `/terminology/:terminologyId(${uuidRegexp})/concept/:conceptId(${uuidRegexp})`,
destination:
'http://localhost:9103/terminology-api/v2/resolve/v1?termedId=:conceptId',
},
{
source: '/terminology-api/:path*',
destination: 'http://localhost:9103/terminology-api/:path*',
Expand All @@ -131,6 +144,16 @@ module.exports = (phase, { defaultConfig }) => {
// https://nextjs.org/docs/api-reference/next.config.js/rewrites
async rewrites() {
return [
{
source: `/terminology/:terminologyId(${uuidRegexp})`,
destination:
'http://yti-terminology-api-v2:9107/terminology-api/v2/resolve/v1?termedId=:terminologyId',
},
{
source: `/terminology/:terminologyId(${uuidRegexp})/concept/:conceptId(${uuidRegexp})`,
destination:
'http://yti-terminology-api-v2:9107/terminology-api/v2/resolve/v1?termedId=:conceptId',
},
{
source: '/terminology-api/:path*',
destination:
Expand Down
10 changes: 7 additions & 3 deletions terminology-ui/public/locales/en/admin.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"changing-statuses": "Changing statuses",
"choose-orgs": "Select contributors",
"choose-term-conjugation": "Choose term conjugation",
"choose-term-equivalency": "Choose term equivalency",
"choose-term-family": "Choose term family",
"choose-term-word-class": "Choose term word class",
"choose-type": "Choose type",
Expand All @@ -78,12 +79,12 @@
"concept-class": "Concept class",
"concept-diagram-or-link": "Concept diagram or other link",
"concept-diagrams-and-sources": "Concept diagrams and sources",
"concept-identifier": "Identifier",
"concept-import": {
"column-missing-language": "Property key is missing language suffix",
"duplicate-key-value": "Column name is already in use",
"prefLabel-column-missing": "prefLabel column is missing",
"prefLabel-row-missing": "preflabel needs to be defined in at least 1 language",
"property-missing-language-suffix": "Property key is missing language suffix",
"status-column-missing": "Status column is missing",
"term-missing-language-suffix": "Term key is missing language suffix",
"terminology-no-language": "Language does not exist in terminology",
"undefined-error": "There was an error importing the concepts",
"value-not-valid": "Value in cell was not valid"
Expand Down Expand Up @@ -125,6 +126,7 @@
"diagramsUri": "One or more diagram's url is not in valid form",
"editorialNote": "At least one editorial note is missing a descriptive text",
"example": "At least one example is missing a descriptive text",
"identifier": "Concept's identifier is undefined",
"language": "Term's language is undefined",
"note": "At least one note is missing a descriptive text",
"prefLabel": "Term's name is undefined",
Expand Down Expand Up @@ -158,6 +160,7 @@
"incorrect-file-type_other": "Incorrect file type. Allowed file typea are {{fileTypes}}.",
"upload-error": "File upload failed. Try again."
},
"finished": "Done",
"from-terminology-manual": "Terminology manual.",
"give-definition": "Give definition",
"has-part-concept": "Has part concept",
Expand All @@ -168,6 +171,7 @@
"import-concepts": "Import concepts",
"import-concepts-description-1": "New concepts can imported with a file. Requirements for the contents of the file can be read from",
"import-concepts-description-2": "If you want to update existing concept or other information of the terminology, choose 'Update terminology with a file' in 'Terminology information and action'.",
"import-concepts-in-progress": "Importing concepts",
"import-concepts-to-terminology": "Import concepts to terminology",
"import-incorrect-excel-file-1": {
"concept-import": "File does not match the specifications of the concept import file. Check the specifications of the file in",
Expand Down
3 changes: 2 additions & 1 deletion terminology-ui/public/locales/en/alert.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@
"error-occurred_remove-concept": "An error occured while removing concept",
"error-occurred_remove-terminology": "An error occured while removing terminology",
"error-occurred_session": "An error occured with the session, try logging in again",
"error-occurred_unauthenticated": "An error occured with login information verification. Please login to the service again."
"error-occurred_unauthenticated": "An error occured with login information verification. Please login to the service again.",
"error-occured_resource-in-use": "Cannot remove because resource is in use"
}
Loading