Skip to content

Uri and json fields #1099 #1103

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

Draft
wants to merge 14 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
9 changes: 7 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"**/node_modules": true,
"**/build": true,
"**/coverage": true,
"**/dist": true,
"**/dist": true
},
"eslint.validate": [
"javascript",
Expand All @@ -32,5 +32,10 @@
"./svelte"
],
"typescript.preferences.preferTypeOnlyAutoImports": true,
"rustTestExplorer.rootCargoManifestFilePath": "./Cargo.toml"
"rustTestExplorer.rootCargoManifestFilePath": "./Cargo.toml",
// This won't work in multi-root workspaces, could be fixed by using a rust-analyzer.toml once there is some more documentation on that.
// For now you need to set this in your own vscode settings file.
"rust-analyzer.cargo.extraEnv": {
"ATOMICSERVER_SKIP_JS_BUILD": "true"
}
}
10 changes: 7 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@ See [STATUS.md](server/STATUS.md) to learn more about which features will remain
- [#1056](https://github.com/atomicdata-dev/atomic-server/issues/1056) Switched from Earthly to Dagger for CI. Also made improvements to E2E test publishing and building docker images.
- [#979](https://github.com/atomicdata-dev/atomic-server/issues/979) Fix nested resource deletion, use transactions
- [#1057](https://github.com/atomicdata-dev/atomic-server/issues/1057) Fix double slashes in search bar
- CLI should use Agent in requests - get #986
- Search endpoint throws error for websocket requests #1047
- Fix search in CLI / atomic_lib #958
- [#986](https://github.com/atomicdata-dev/atomic-server/issues/986) CLI should use Agent in requests - get
- [#1047](https://github.com/atomicdata-dev/atomic-server/issues/1047) Search endpoint throws error for websocket requests
- [#958](https://github.com/atomicdata-dev/atomic-server/issues/958) Fix search in CLI / atomic_lib
- [#658](https://github.com/atomicdata-dev/atomic-server/issues/658) Added JSON datatype.
- [#1024](https://github.com/atomicdata-dev/atomic-server/issues/1024) Added URI datatype.
BREAKING: [#1107](https://github.com/atomicdata-dev/atomic-server/issues/1107) Named nested resources are no longer supported. Value::Resource and SubResource::Resource have been removed. If you need to include multiple resources in a response use an array.
BREAKING: `store.get_resource_extended()` now returns a `ResourceResponse` instead of a `Resource` due to the removal of named nested resources.

## [v0.40.2]

Expand Down
25 changes: 24 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions browser/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ This changelog covers all five packages, as they are (for now) updated as a whol
- [#983](https://github.com/atomicdata-dev/atomic-server/issues/983) Give clear error when name collisions are found in an ontology.
- Generates class definitions that enables doing: `resource.props.name = 'New Name'`;
- [#1071](https://github.com/atomicdata-dev/atomic-server/issues/1071) Fix bug where classes and properties with 'name' props would lead to invalid generated typescript code.
- Generated ontologies now base import extensions on the tsconfig.json file. (moduleResolution: bundler will remove the .js extensions in imports)

### @tomic/svelte

Expand Down
5 changes: 1 addition & 4 deletions browser/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@
"dependencies": {
"@tomic/lib": "workspace:*",
"chalk": "^5.3.0",
"get-tsconfig": "^4.8.1",
"prettier": "3.0.3"
},
"devDependencies": {
"prettier": "3.0.3",
"typescript": "^5.6.3"
},
"description": "Generate types from Atomic Data ontologies",
Expand Down
2 changes: 2 additions & 0 deletions browser/cli/src/DatatypeToTSTypeMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,7 @@ export const DatatypeToTSTypeMap = {
[Datatype.STRING]: 'string',
[Datatype.SLUG]: 'string',
[Datatype.MARKDOWN]: 'string',
[Datatype.URI]: 'string',
[Datatype.JSON]: 'unknown',
[Datatype.UNKNOWN]: 'JSONValue',
};
2 changes: 1 addition & 1 deletion browser/cli/src/PropertyRecord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export class PropertyRecord {
]);
}

public repordPropertyDefined(subject: string) {
public reportPropertyDefined(subject: string) {
this.knownProperties.add(subject);

if (this.missingProperties.has(subject)) {
Expand Down
2 changes: 1 addition & 1 deletion browser/cli/src/generateOntology.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const generateOntology = async (
const properties = dedupe(ontology.props.properties ?? []);

for (const prop of properties) {
propertyRecord.repordPropertyDefined(prop);
propertyRecord.reportPropertyDefined(prop);
}

const [baseObjStr, reverseMapping] = await generateBaseObject(ontology);
Expand Down
56 changes: 49 additions & 7 deletions browser/cli/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { getTsconfig } from 'get-tsconfig';
import { sys as tsSys, findConfigFile, readConfigFile } from 'typescript';

const NOT_FOUND = 'tsconfig.json not found';
const COULD_NOT_READ = 'Could not read tsconfig.json';

export const camelCaseify = (str: string) =>
str.replace(/-([a-z0-9])/g, g => {
Expand All @@ -11,12 +14,51 @@ export const dedupe = <T>(array: T[]): T[] => {

export const getExtension = () => {
try {
return getTsconfig()?.config.compilerOptions?.moduleResolution === 'Bundler'
? ''
: '.js';
} catch (e) {
console.warn('Something went wrong getting TS Config / file extension', e);
const tsconfig = getTsconfig();
const moduleResolution = tsconfig.config.compilerOptions?.moduleResolution;

if (!moduleResolution) {
return '.js';
}

return moduleResolution.toLowerCase() === 'bundler' ? '' : '.js';
} catch (error) {
if (error instanceof Error) {
if (error.message === NOT_FOUND) {
// eslint-disable-next-line no-console
console.log('tsconfig.json not found, defaulting to .js imports');

return '.js';
}

if (error.message === COULD_NOT_READ) {
// eslint-disable-next-line no-console
console.log('Could not read tsconfig.json, defaulting to .js imports');

return '.js';
}

return '.js';
throw error;
} else {
throw new Error(error);
}
}
};

const getTsconfig = () => {
// Find tsconfig.json file
const tsconfigPath = findConfigFile(
process.cwd(),
tsSys.fileExists,
'tsconfig.json',
);

if (!tsconfigPath) throw new Error(NOT_FOUND);

// Read tsconfig.json file
const tsconfigFile = readConfigFile(tsconfigPath, tsSys.readFile);

if (!tsconfigFile.config) throw new Error(COULD_NOT_READ);

return tsconfigFile;
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
Expand Down
5 changes: 5 additions & 0 deletions browser/data-browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"@bugsnag/core": "^7.25.0",
"@bugsnag/js": "^7.25.0",
"@bugsnag/plugin-react": "^7.25.0",
"@codemirror/lang-json": "^6.0.2",
"@codemirror/lint": "^6.8.5",
"@dagrejs/dagre": "^1.1.4",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/sortable": "^8.0.0",
Expand All @@ -27,6 +29,9 @@
"@tiptap/starter-kit": "^2.9.1",
"@tiptap/suggestion": "^2.9.1",
"@tomic/react": "workspace:*",
"@uiw/codemirror-theme-github": "^4.24.1",
"@uiw/react-codemirror": "^4.24.1",
"clsx": "^2.1.1",
"emoji-mart": "^5.6.0",
"polished": "^4.3.1",
"prismjs": "^1.29.0",
Expand Down
152 changes: 152 additions & 0 deletions browser/data-browser/src/chunks/CodeEditor/AsyncJSONEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import CodeMirror, {
type BasicSetupOptions,
type EditorView,
type ReactCodeMirrorRef,
} from '@uiw/react-codemirror';
import { githubLight, githubDark } from '@uiw/codemirror-theme-github';
import { json, jsonParseLinter } from '@codemirror/lang-json';
import { linter, type Diagnostic } from '@codemirror/lint';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { styled, useTheme } from 'styled-components';

export interface JSONEditorProps {
labelId?: string;
initialValue?: string;
showErrorStyling?: boolean;
required?: boolean;
maxWidth?: string;
autoFocus?: boolean;
onChange: (value: string) => void;
onValidationChange?: (isValid: boolean) => void;
onBlur?: () => void;
}

const basicSetup: BasicSetupOptions = {
lineNumbers: false,
foldGutter: false,
highlightActiveLine: true,
indentOnInput: true,
};

/**
* ASYNC COMPONENT DO NOT IMPORT DIRECTLY, USE {@link JSONEditor.tsx}.
*/
const AsyncJSONEditor: React.FC<JSONEditorProps> = ({
labelId,
initialValue,
showErrorStyling,
required,
maxWidth,
autoFocus,
onChange,
onValidationChange,
onBlur,
}) => {
const editorRef = useRef<ReactCodeMirrorRef>(null);
const theme = useTheme();
const [value, setValue] = useState(initialValue ?? '');
const latestDiagnostics = useRef<Diagnostic[]>([]);
// We need to use callback because the compiler can't optimize the CodeMirror component.
const handleChange = useCallback(
(val: string) => {
setValue(val);
onChange(val);
},
[onChange],
);

// Wrap jsonParseLinter so we can tap into diagnostics
const validationLinter = useCallback(() => {
const delegate = jsonParseLinter();

return (view: EditorView) => {
const isEmpty = view.state.doc.length === 0;
let diagnostics = delegate(view);

if (!required && isEmpty) {
diagnostics = [];
}

// Compare the diagnostics so we don't call the onValidationChange callback unnecessarily.
const prev = latestDiagnostics.current;
const changed =
diagnostics.length !== prev.length ||
diagnostics.some(
(d, i) => d.from !== prev[i]?.from || d.message !== prev[i]?.message,
);

if (changed) {
latestDiagnostics.current = diagnostics;
onValidationChange?.(diagnostics.length === 0);
}

return diagnostics;
};
}, [onValidationChange]);

const extensions = useMemo(
// eslint-disable-next-line react-compiler/react-compiler
() => [json(), linter(validationLinter())],
[validationLinter],
);

useEffect(() => {
// The actual editor is not mounted immediately so we need to wait a cycle.
requestAnimationFrame(() => {
if (editorRef.current?.editor && labelId) {
const realEditor =
editorRef.current.editor.querySelector('.cm-content');

if (!realEditor) {
return;
}

realEditor.setAttribute('aria-labelledby', labelId);
}
});
}, [labelId]);

return (
<CodeEditorWrapper
onBlur={() => onBlur?.()}
className={showErrorStyling ? 'json-editor__error' : ''}
>
<CodeMirror
ref={editorRef}
autoFocus={autoFocus}
value={value}
onChange={handleChange}
placeholder='Enter valid JSON...'
// We disable tab indenting because that would mess with accessibility/keyboard navigation.
indentWithTab={false}
theme={theme.darkMode ? githubDark : githubLight}
minHeight='150px'
maxHeight='40rem'
maxWidth={maxWidth ?? '100%'}
basicSetup={basicSetup}
extensions={extensions}
/>
</CodeEditorWrapper>
);
};

export default AsyncJSONEditor;

const CodeEditorWrapper = styled.div`
display: contents;

&.json-editor__error .cm-editor {
border-color: ${p => p.theme.colors.alert} !important;
}

& .cm-editor {
border: 1px solid ${p => p.theme.colors.bg2};
border-radius: ${p => p.theme.radius};
/* padding: ${p => p.theme.size(2)}; */
outline: none;

&:focus-within {
border-color: ${p => p.theme.colors.main};
}
}
`;
Loading
Loading