diff --git a/browser/CHANGELOG.md b/browser/CHANGELOG.md index c87180931..f2ec28847 100644 --- a/browser/CHANGELOG.md +++ b/browser/CHANGELOG.md @@ -13,6 +13,7 @@ This changelog covers all three packages, as they are (for now) updated as a who ### @tomic/lib +- [#840](https://github.com/atomicdata-dev/atomic-server/issues/840) Added `store.search()`. - Deprecated `resource.getSubject()` in favor of `resource.subject`. - Deprecated `store.getResouceAsync()` in favor of `store.getResource()`. - Deprecated `resource.pushPropval()` in favor of `resource.push()`. @@ -34,6 +35,13 @@ This changelog covers all three packages, as they are (for now) updated as a who - `resource.removeClasses()` - `resource.addClasses()` +### @tomic/cli + +- [#837](https://github.com/atomicdata-dev/atomic-server/issues/837) Fix timestamp is mapped to string instead of number. +- [#831](https://github.com/atomicdata-dev/atomic-server/issues/831) Give clear error when trying to generate types from a non ontology resource +- [#830](https://github.com/atomicdata-dev/atomic-server/issues/830) Create output folder if it doesn't exist +- Use type import in generated files. + ## v0.37.0 ### Atomic Browser diff --git a/browser/cli/src/DatatypeToTSTypeMap.ts b/browser/cli/src/DatatypeToTSTypeMap.ts index 0c0a0a13c..3c4a0b5f4 100644 --- a/browser/cli/src/DatatypeToTSTypeMap.ts +++ b/browser/cli/src/DatatypeToTSTypeMap.ts @@ -5,7 +5,7 @@ export const DatatypeToTSTypeMap = { [Datatype.RESOURCEARRAY]: 'string[]', [Datatype.BOOLEAN]: 'boolean', [Datatype.DATE]: 'string', - [Datatype.TIMESTAMP]: 'string', + [Datatype.TIMESTAMP]: 'number', [Datatype.INTEGER]: 'number', [Datatype.FLOAT]: 'number', [Datatype.STRING]: 'string', diff --git a/browser/cli/src/commands/ontologies.ts b/browser/cli/src/commands/ontologies.ts index c456c49a4..23fc83197 100644 --- a/browser/cli/src/commands/ontologies.ts +++ b/browser/cli/src/commands/ontologies.ts @@ -8,19 +8,27 @@ import { atomicConfig } from '../config.js'; import { generateIndex } from '../generateIndex.js'; import { PropertyRecord } from '../PropertyRecord.js'; import { generateExternals } from '../generateExternals.js'; +import { validateOntologies } from '../validateOntologies.js'; export const ontologiesCommand = async (_args: string[]) => { const propertyRecord = new PropertyRecord(); console.log( - chalk.blue( - `Found ${chalk.red( - Object.keys(atomicConfig.ontologies).length, - )} ontologies`, - ), + chalk.blue(`Found ${chalk.red(atomicConfig.ontologies.length)} ontologies`), ); - for (const subject of Object.values(atomicConfig.ontologies)) { + const [valid, report] = await validateOntologies(atomicConfig.ontologies); + + if (!valid) { + console.log(chalk.red('Could not generate ontologies')); + console.log(report); + + return; + } + + checkOrCreateFolder(atomicConfig.outputFolder); + + for (const subject of atomicConfig.ontologies) { await write(await generateOntology(subject, propertyRecord)); } @@ -73,3 +81,9 @@ const write = async ({ console.log(chalk.blue('Wrote to'), chalk.cyan(filePath)); }; + +const checkOrCreateFolder = (relativePath: string): void => { + const fullPath = path.join(process.cwd(), relativePath); + + fs.mkdirSync(fullPath, { recursive: true }); +}; diff --git a/browser/cli/src/generateOntology.ts b/browser/cli/src/generateOntology.ts index 5ebc860c5..493bb1829 100644 --- a/browser/cli/src/generateOntology.ts +++ b/browser/cli/src/generateOntology.ts @@ -2,7 +2,6 @@ import { generateBaseObject } from './generateBaseObject.js'; import { generateClasses } from './generateClasses.js'; import { store } from './store.js'; import { camelCaseify } from './utils.js'; -// TODO: Replace with actual project config file. import { generatePropTypeMapping } from './generatePropTypeMapping.js'; import { generateSubjectToNameMapping } from './generateSubjectToNameMapping.js'; import { generateClassExports } from './generateClassExports.js'; @@ -26,7 +25,7 @@ const TEMPLATE = ` * For more info on how to use ontologies: https://github.com/atomicdata-dev/atomic-server/blob/develop/browser/cli/readme.md * -------------------------------- */ -import { BaseProps } from '${Inserts.MODULE_ALIAS}' +import type { BaseProps } from '${Inserts.MODULE_ALIAS}' ${Inserts.BASE_OBJECT} diff --git a/browser/cli/src/validateOntologies.ts b/browser/cli/src/validateOntologies.ts new file mode 100644 index 000000000..8534cafb9 --- /dev/null +++ b/browser/cli/src/validateOntologies.ts @@ -0,0 +1,29 @@ +import { core } from '@tomic/lib'; +import { store } from './store.js'; +import chalk from 'chalk'; + +export const validateOntologies = async ( + ontologies: string[], +): Promise<[valid: boolean, report: string]> => { + let isValid = true; + let report = ''; + + for (const subject of ontologies) { + try { + const resource = await store.getResourceAsync(subject); + + if (!resource.hasClasses(core.classes.ontology)) { + isValid = false; + const isA = await store.getResourceAsync(resource.getClasses()[0]); + report += `Expected ${chalk.cyan( + resource.title, + )} to have class Ontology but found ${chalk.cyan(isA.title)}\n`; + } + } catch (e) { + isValid = false; + report += `Could not fetch ontology at ${subject}\n`; + } + } + + return [isValid, report]; +}; diff --git a/browser/lib/src/store.ts b/browser/lib/src/store.ts index 0ada27b84..024f90c12 100644 --- a/browser/lib/src/store.ts +++ b/browser/lib/src/store.ts @@ -22,6 +22,9 @@ import { commits, collections, JSONValue, + SearchOpts, + buildSearchSubject, + server, } from './index.js'; import { authenticate, fetchWebSocket, startWebsocket } from './websockets.js'; @@ -232,6 +235,16 @@ export class Store { return resource; } + public async search(query: string, opts: SearchOpts = {}): Promise { + const searchSubject = buildSearchSubject(this.serverUrl, query, opts); + const searchResource = await this.fetchResourceFromServer(searchSubject, { + noWebSocket: true, + }); + const results = searchResource.get(server.properties.results) ?? []; + + return results; + } + /** Checks if a subject is free to use */ public async checkSubjectTaken(subject: string): Promise { const r = this.resources.get(subject); diff --git a/browser/package.json b/browser/package.json index 381888d2f..ea861ac7b 100644 --- a/browser/package.json +++ b/browser/package.json @@ -61,7 +61,7 @@ "cli" ] }, - "packageManager": "pnpm@8.6.12", + "packageManager": "pnpm@8.15.2", "dependencies": { "eslint-plugin-import": "^2.26.0" } diff --git a/browser/svelte/package.json b/browser/svelte/package.json index 2d27a4ac2..c6f4907a5 100644 --- a/browser/svelte/package.json +++ b/browser/svelte/package.json @@ -26,7 +26,6 @@ "module": "./dist/index.js", "main-dev": "src/index.ts", "name": "@tomic/svelte", - "packageManager": "pnpm@7.13.3", "peerDependencies": { "@tomic/lib": "workspace:*" }, diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 01bdc3a37..88eb3ceab 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -10,8 +10,9 @@ # AtomicServer - [AtomicServer](atomic-server.md) - - [When (not) to use it](atomicserver/when-to-use.md) + - [When (not) to use it](atomicserver/when-to-use.md))] - [Installation](atomicserver/installation.md) + - [FAQ & troubleshooting](atomicserver/faq.md) - [Getting started with the GUI](atomicserver/gui.md) - [API](atomicserver/API.md) - [Creating a JSON-AD file](create-json-ad.md) @@ -30,9 +31,23 @@ - [Rust lib](rust-lib.md) - [Rust CLI](rust-cli.md) -# Specification (core) +# Guides + +- [Build a portfolio using Astro and Atomic Server](astro-guide/1-index.md) + - [Setup](astro-guide/2-setup.md) + - [Frontend setup](astro-guide/3-frontend-setup.md) + - [Basic data model](astro-guide/4-basic-data-model.md) + - [Creating homepage data](astro-guide/5-creating-homepage-data.md) + - [Generating types](astro-guide/6-generating-types.md) + - [Fetching data](astro-guide/7-fetching-data.md) + - [Using ResourceArray to display a list of projects](astro-guide/8-pojects.md) + - [Using Collections to build the blogs page](astro-guide/9-blogs.md) + - [Using the search API to build a search bar](astro-guide/10-search.md) + +# Specification + +- [Atomic Data Core](core/concepts.md) -- [What is Atomic Data?](core/concepts.md) - [Serialization](core/serialization.md) - [JSON-AD](core/json-ad.md) - [Querying](core/querying.md) @@ -42,8 +57,6 @@ - [Datatypes](schema/datatypes.md) - [FAQ](schema/faq.md) -# Specification (extended) - - [Atomic Data Extended](extended.md) - [Agents](agents.md) - [Hierarchy and authorization](hierarchy.md) @@ -70,7 +83,6 @@ - [Graph Databases](interoperability/graph-database.md) - [Potential use cases](usecases/intro.md) - [As a Headless CMS](usecases/headless-cms.md) - - [In a React project](usecases/react.md) - [Personal Data Store](usecases/personal-data-store.md) - [Artificial Intelligence](usecases/ai.md) - [E-commerce & marketplaces](usecases/e-commerce.md) diff --git a/docs/src/acknowledgements.md b/docs/src/acknowledgements.md index e88ec6b7d..9dbe602a1 100644 --- a/docs/src/acknowledgements.md +++ b/docs/src/acknowledgements.md @@ -3,6 +3,7 @@ ## Authors: - **Joep Meindertsma** ([joepio](https://github.com/joepio/) from [Ontola.io](https://ontola.io/)) +- **Polle Pas** ([polleps](https://github.com/polleps/) from [Ontola.io](https://ontola.io/)) ## Special thanks to: diff --git a/docs/src/astro-guide/1-index.md b/docs/src/astro-guide/1-index.md new file mode 100644 index 000000000..d31b94e28 --- /dev/null +++ b/docs/src/astro-guide/1-index.md @@ -0,0 +1,16 @@ +# Creating a portfolio website using Astro and Atomic Server + +Atomic Server is a great fit for a headless CMS because it works seamlessly on the server and client while providing a top-notch developer experience. +In this guide, we will build a portfolio site using [Astro](https://astro.build/) to serve and build our pages and use Atomic Data to hold our data. + +Astro is a web framework for creating fast multi-page applications using web technology. +It plays very nicely with the `@tomic/lib` client library. + +There are a few things that won't be covered in this guide like styling and CSS. +There will be very minimal use of CSS in this guide so we can focus on the technical parts of the website. +Feel free to spice it up and add some styling while following along though. + +I will also not cover every little detail about Astro, only what is necessary to follow along with this guide. +If you're completely new to Astro consider skimming the [documentation](https://docs.astro.build/en/getting-started/) to see what it has to offer. + +With all that out of the way let's start by setting up your atomic data server. If you already have a server running skip to [Creating the frontend](3-frontend-setup.md) diff --git a/docs/src/astro-guide/10-search.md b/docs/src/astro-guide/10-search.md new file mode 100644 index 000000000..96c84298b --- /dev/null +++ b/docs/src/astro-guide/10-search.md @@ -0,0 +1,194 @@ +# Making a search bar for blogposts + +## Using the search API + +AtomicServer comes with a fast full-text search API out of the box. +@tomic/lib provides some convenient helper functions on Store to make using this API very easy. + +To use search all you need to do is: + +```typescript +const results = await store.search('how to make icecream'); +``` + +The method returns an array of subjects of resources that match the given query. + +To further refine the query, we can pass a filter object to the method like so: + +```typescript +const results = await store.search('how to make icecream', { + filters: { + [core.properties.isA]: myPortfolio.classes.blogpost, + }, +}); +``` + +This way the result will only include resources that have an `is-a` of `blogpost`. + +## Running code on the client + +To make a working search bar, we will have to run code on the client. +Astro code only runs on the server but there are a few ways to have code run on the client. +The most commonly used option would be to use a frontend framework like React or Svelte but Astro also allows script tags to be added to components that will be included in the `` of the page. + +To keep this guide framework-agnostic we will use a script tag and a web component but feel free to use any framework you're more comfortable with, the code should be simple enough to adapt to different frameworks. + +First, we need to make a change to our environment variables because right now they are not available to the client and therefore `getStore` will not be able to access `ATOMIC_SERVER_URL`. +To make an environment variable accessible to the client it needs to be prefixed with `PUBLIC_`. + +In `.env` change `ATOMIC_SERVER_URL` to `PUBLIC_ATOMIC_SERVER_URL`. + +```env +// .env +PUBLIC_ATOMIC_SERVER_URL= +ATOMIC_HOMEPAGE_SUBJECT= +``` + +Now update `src/helpers/getStore.ts` to reflect the name change. + +```typescript +// src/helpers/getStore.ts +import { Store } from '@tomic/lib'; +import { initOntologies } from '../ontologies'; + +let store: Store; + +export function getStore(): Store { + if (!store) { + store = new Store({ + serverUrl: import.meta.env.PUBLIC_ATOMIC_SERVER_URL, + }); + + initOntologies(); + } + + return store; +} +``` + +## Creating the search bar + +In `src/components` create a file called `Search.astro`. + +```html + + + +``` + +If you've never seen web components before, `` is our custom element that starts as just an empty shell. +We then add a `