Skip to content

Commit a2a23dd

Browse files
Fix: #24. Global ontoscore button (#29)
* global ontoscore button * Bump version * fix empty bundle downloaded * icon in global ontoscore button * updated ontoscore colors, added global ontoscore hook and ontoscore color hook --------- Co-authored-by: Stefano Baruzzo <[email protected]>
1 parent 8c27247 commit a2a23dd

File tree

12 files changed

+485
-200
lines changed

12 files changed

+485
-200
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@italia/dati-semantic-schema-editor",
3-
"version": "0.0.6",
3+
"version": "0.0.7",
44
"description": "",
55
"main": "index.js",
66
"keywords": [],

packages/schema-editor/src/plugins/json-schema-5/components/actions-menu.spec.ts

Lines changed: 0 additions & 99 deletions
This file was deleted.
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { render, waitFor } from '@testing-library/react';
2+
import { Map } from 'immutable';
3+
import yaml from 'js-yaml';
4+
import { beforeEach, describe, expect, it, vi } from 'vitest';
5+
import * as configuration from '../../configuration/hooks';
6+
import { ActionsMenu, createBundle } from './actions-menu';
7+
8+
describe('ActionsMenu', () => {
9+
const sparqlUrl = 'https://virtuoso-test-external-service-ndc-test.apps.cloudpub.testedev.istat.it/sparql';
10+
11+
beforeEach(() => {
12+
vi.spyOn(configuration, 'useConfiguration').mockReturnValue({
13+
oasCheckerUrl: 'https://test.com',
14+
schemaEditorUrl: 'https://test.com',
15+
sparqlUrl,
16+
});
17+
});
18+
19+
it('should createBundle', async () => {
20+
const specYaml = `openapi: 3.0.3
21+
components:
22+
schemas:
23+
Country:
24+
x-jsonld-type: Country
25+
x-jsonld-context:
26+
'@vocab': https://w3id.org/italia/onto/CLV/
27+
country: '@id'
28+
'@base': 'https://publications.europa.eu/resource/authority/country/'
29+
type: object
30+
properties:
31+
country:
32+
type: string
33+
example:
34+
country: ITA
35+
Location:
36+
x-jsonld-context:
37+
'@vocab': https://w3id.org/italia/onto/CLV/
38+
country: hasCountry
39+
type: object
40+
properties:
41+
hasCity:
42+
type: string
43+
country:
44+
$ref: '#/components/schemas/Country'
45+
example:
46+
hasCity: Rome
47+
country:
48+
country: ITA`;
49+
const specJson = yaml.load(specYaml) as any;
50+
const bundledSpecJson = await createBundle(specJson, { sparqlUrl });
51+
expect(bundledSpecJson).toBeTruthy();
52+
expect(bundledSpecJson['info']['x-ontoscore']).toEqual(1);
53+
});
54+
55+
it('should download bundle when clicking download button', async () => {
56+
const specYaml = `openapi: 3.0.3
57+
components:
58+
schemas:
59+
Country:
60+
x-jsonld-type: Country
61+
x-jsonld-context:
62+
'@vocab': https://w3id.org/italia/onto/CLV/
63+
country: '@id'
64+
'@base': 'https://publications.europa.eu/resource/authority/country/'
65+
type: object
66+
properties:
67+
country:
68+
type: string
69+
example:
70+
country: ITA
71+
Location:
72+
x-jsonld-context:
73+
'@vocab': https://w3id.org/italia/onto/CLV/
74+
country: hasCountry
75+
type: object
76+
properties:
77+
hasCity:
78+
type: string
79+
country:
80+
$ref: '#/components/schemas/Country'
81+
example:
82+
hasCity: Rome
83+
country:
84+
country: ITA`;
85+
const specJson = yaml.load(specYaml) as any;
86+
const specJsonMap = Map(specJson);
87+
const specSelectors = {
88+
specJson: () => specJsonMap,
89+
};
90+
91+
const mockDump = vi.spyOn(yaml, 'dump');
92+
93+
const { getByText } = render(<ActionsMenu specSelectors={specSelectors} url={''} specActions={{}} />);
94+
95+
const downloadButton = getByText('Download bundle');
96+
downloadButton.click();
97+
await waitFor(() => {
98+
expect(mockDump).toHaveBeenCalledWith(expect.objectContaining({ info: { 'x-ontoscore': 1 } }));
99+
});
100+
});
101+
});

packages/schema-editor/src/plugins/json-schema-5/components/actions-menu.tsx

Lines changed: 8 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,11 @@
11
/**
22
* A menu with right icons, see https://italia.github.io/design-react-kit/?path=/docs/documentazione-componenti-dropdown--documentazione#menu-icona-a-destra
33
*/
4-
import { Dropdown, DropdownToggle } from 'design-react-kit';
5-
6-
import { DropdownMenu, Icon, LinkList, LinkListItem } from 'design-react-kit';
7-
import { fromJS, Map } from 'immutable';
4+
import { Dropdown, DropdownMenu, DropdownToggle, Icon, LinkList, LinkListItem } from 'design-react-kit';
85
import yaml from 'js-yaml';
96
import { useConfiguration } from '../../configuration';
10-
import { resolveJsonldContext } from '../../jsonld-context/resolve-jsonld-context';
11-
import {
12-
buildOntoScoreSparqlQuery,
13-
compressAndBase64UrlSafe,
14-
copyToClipboard,
15-
determinePropertiesToValidate,
16-
fetchValidOntoScorePropertiesCount,
17-
normalizeOpenAPISpec,
18-
resolveOpenAPISpec,
19-
} from '../utils';
7+
import { compressAndBase64UrlSafe, copyToClipboard, normalizeOpenAPISpec } from '../utils';
8+
import { calculateGlobalOntoscore } from '../utils/onto-score';
209

2110
const copyAsB64zipToClipboard = (text: string, prefix: string = '') => {
2211
copyToClipboard(`${prefix}${compressAndBase64UrlSafe(text)}`);
@@ -34,87 +23,14 @@ const downloadContent = (content: any, mediaType: string, fileName: string) => {
3423
URL.revokeObjectURL(url);
3524
};
3625

37-
export const createBundle = async (specJson: object, options: { sparqlUrl: string }) => {
38-
// Resolve openapi spec with external references
39-
let resolvedSpecJson = await resolveOpenAPISpec(specJson);
40-
const resolvedSpecOrderedMap = fromJS(resolvedSpecJson);
41-
42-
// Extract all data models from spec
43-
const dataModels = resolvedSpecOrderedMap.getIn(['components', 'schemas']) as Map<any, any> | undefined;
44-
if (!dataModels) {
45-
return 'No #/components/schemas models provided';
46-
}
47-
48-
// Calculate specific and global ontoscores
49-
let globalOntoScoreModels = 0;
50-
let globalOntoScoreSum = 0;
51-
52-
const setOntoscoreValue = (dataModelKey: string, value: number) => {
53-
resolvedSpecJson['components']['schemas'][dataModelKey]['x-ontoscore'] = value;
54-
globalOntoScoreSum += value;
55-
globalOntoScoreModels++;
56-
};
57-
58-
// Process every datamodel
59-
for (const [dataModelKey, dataModel] of dataModels.entries()) {
60-
// Filter only data models with type "object"
61-
const isObject = (dataModel.get('type', '') as string | undefined)?.toLowerCase() === 'object';
62-
if (!isObject) {
63-
continue;
64-
}
65-
66-
// Extract x-jsonld-context if present
67-
if (!dataModel.has('x-jsonld-context')) {
68-
setOntoscoreValue(dataModelKey, 0);
69-
continue;
70-
}
71-
72-
// Resolve x-jsonld-context
73-
const jsonldContext = resolveJsonldContext(dataModel)?.get('@context');
74-
if (!jsonldContext) {
75-
setOntoscoreValue(dataModelKey, 0);
76-
continue;
77-
}
78-
79-
// Determine data model's properties to use for ontoscore calculation
80-
const propertiesPaths: string[][] =
81-
dataModel
82-
.get('properties')
83-
?.keySeq()
84-
.toArray()
85-
.map((x) => [x]) || [];
86-
87-
// Determine properties to validate
88-
const { valid: validPropertiesPaths, unknown: unknownPropertiesPaths } = await determinePropertiesToValidate(
89-
jsonldContext,
90-
propertiesPaths,
91-
);
92-
93-
// Execute sparql fetch to check if mapped onto-properties are correct
94-
const sparqlResultCount = await fetchValidOntoScorePropertiesCount(unknownPropertiesPaths, {
95-
sparqlUrl: options.sparqlUrl,
96-
});
97-
const semanticPropertiesCount = validPropertiesPaths.length + sparqlResultCount;
98-
const rawPropertiesCount = propertiesPaths?.length;
99-
const score = rawPropertiesCount > 0 ? semanticPropertiesCount / rawPropertiesCount : 0;
100-
101-
setOntoscoreValue(dataModelKey, score);
102-
}
103-
104-
// Setting global onto score (calculated as an average ontoscore value)
105-
if (!resolvedSpecJson['info']) {
106-
resolvedSpecJson['info'] = {};
107-
}
108-
resolvedSpecJson['info']['x-ontoscore'] = globalOntoScoreSum / globalOntoScoreModels;
109-
110-
// Normalize x-ref elements
26+
export const createBundle = async (specJson: object, options: { sparqlUrl: string }): Promise<object> => {
27+
let { resolvedSpecJson } = await calculateGlobalOntoscore(specJson, { sparqlUrl: options.sparqlUrl });
11128
resolvedSpecJson = normalizeOpenAPISpec(resolvedSpecJson);
112-
11329
return resolvedSpecJson;
11430
};
11531

11632
export const ActionsMenu = ({ specSelectors, url, specActions }) => {
117-
const { oasCheckerUrl, schemaEditorUrl, sparqlUrl } = useConfiguration();
33+
const { oasCheckerUrl, schemaEditorUrl, sparqlUrl = '' } = useConfiguration();
11834

11935
const actions: Array<{
12036
text: string;
@@ -146,8 +62,8 @@ export const ActionsMenu = ({ specSelectors, url, specActions }) => {
14662
text: 'Download bundle',
14763
icon: 'it-download',
14864
onClick: async () => {
149-
const resolvedSpec = await createBundle(specSelectors.specJson().toJS(), { sparqlUrl: sparqlUrl as string });
150-
downloadContent(yaml.dump(resolvedSpec), 'application/yaml', 'spec.yaml');
65+
const resolvedSpecJson = await createBundle(specSelectors.specJson().toJS(), { sparqlUrl });
66+
downloadContent(yaml.dump(resolvedSpecJson), 'application/yaml', 'spec.yaml');
15167
},
15268
},
15369
{

packages/schema-editor/src/plugins/json-schema-5/components/common/onto-score-block.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Badge, Spinner } from 'design-react-kit';
2-
import { useOntoScore } from '../../hooks';
32
import { Map } from 'immutable';
3+
import { useOntoScore, useOntoScoreColor } from '../../hooks';
44

55
interface Props {
66
jsonldContext: Map<string, any> | undefined;
@@ -13,6 +13,7 @@ export function OntoScoreBlock({ jsonldContext, propertiesPaths }: Props) {
1313
error,
1414
data: { rawPropertiesCount, semanticPropertiesCount, score },
1515
} = useOntoScore(jsonldContext, propertiesPaths);
16+
const ontoscoreColor = useOntoScoreColor(score ?? 0);
1617

1718
return !jsonldContext || !propertiesPaths ? (
1819
<></>
@@ -29,7 +30,7 @@ export function OntoScoreBlock({ jsonldContext, propertiesPaths }: Props) {
2930
</Badge>
3031
) : (
3132
<Badge
32-
color={score > 0.9 ? 'success' : score > 0.5 ? 'warning' : 'danger'}
33+
color={ontoscoreColor}
3334
title={`A beta feature showing the ratio of semantic annotated properties to total properties (${semanticPropertiesCount}/${rawPropertiesCount}).`}
3435
>
3536
<small>OntoScore &beta;: {score.toFixed(2)}</small>

0 commit comments

Comments
 (0)