Skip to content

Contentful integration #6

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

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules/
.yarn/
.yarn/
*.log
5 changes: 5 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.gitkeep
.env*
*.ico
*.lock
README.md
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"editor.tabSize": 2,
"editor.detectIndentation": false,
"prettier.tabWidth": 2
}
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,30 @@ Start the application with the script from the root folder
```shell
yarn dev:sb
```

## Contentful Development

### Installation

To install the dependencies, run the following command from the root folder:

```sh
yarn
```

### Environment Setup

Create a `.env` file inside the `contentful-ai-toolkit` package directory. Add the environment variable `REACT_APP_OPENAI_TOKEN` with your valid OpenAI token:

```dosini
# .env
REACT_APP_OPENAI_TOKEN=sk-**********************************
```

### Running the Development Server

Start the development server by executing the following command from the root folder:

```sh
yarn dev:cf
```
19 changes: 0 additions & 19 deletions lerna-debug.log

This file was deleted.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"scripts": {
"dev": "lerna run pre-dev-sdk && lerna run pre-dev-sanity-sdk && lerna run pre-dev-sanity-ai-tookit && lerna run --parallel dev",
"dev:sb": "lerna run pre-dev-sdk && lerna run pre-dev-sb-sdk && lerna run pre-dev-sb-storage-sdk && lerna run --parallel dev:sb",
"dev:cf": "lerna run pre-dev-sdk && lerna run pre-dev-contentful-sdk && lerna run --parallel dev:cf",
"dev:ngrok": "cd ./packages/storyblok-ai-tookit && ngrok http 3000",
"clean": "lerna run --parallel yalc-clean"
},
Expand All @@ -17,4 +18,4 @@
"repository": "[email protected]:focusreactive/headless-cms-gpt-sdk.git",
"author": "Alex Hramovich <[email protected]>",
"license": "MIT"
}
}
7 changes: 7 additions & 0 deletions packages/contentful-ai-sdk/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
};
28 changes: 28 additions & 0 deletions packages/contentful-ai-sdk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

# Yalc
.yalc
yalc.lock
9 changes: 9 additions & 0 deletions packages/contentful-ai-sdk/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"bracketSpacing": true,
"printWidth": 100,
"semi": true,
"singleQuote": true,
"trailingComma": "all",
"singleAttributePerLine": true,
"tabWidth": 2
}
170 changes: 170 additions & 0 deletions packages/contentful-ai-sdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
<a href="https://focusreactive.com/" align="center">
<img width="25%" height="auto" src="https://cdn.sanity.io/images/vftxng62/production/25e191578a3c3d4ddfaf69c5f6f7070aead0bff4-507x168.png?auto=format" alt="FocusReactive logo">
</a>

# Contentful AI SDK

## Installation

To add the SDK to your project, run the following command:

```sh
yarn add @focus-reactive/contentful-ai-sdk
```

## Usage

### Initialization

Before using the SDK, you must initialize it with the Contentful client and a valid OpenAI token.
In React apps, you can use the `useSDK` hook from the `@contentful/react-apps-toolkit` package:

```typescript
import type { SidebarAppSDK } from '@contentful/app-sdk'
import { useSDK } from '@contentful/react-apps-toolkit'
import { initSDK } from '@focus-reactive/contentful-ai-sdk'
import { useEffect } from 'react'

const LocationComponent = () => {
const sdk = useSDK<SidebarAppSDK>()

useEffect(() => {
initSDK({ client: sdk.cma, openAiKey: process.env.REACT_APP_OPENAI_TOKEN! })
}, [])

return ...;
}

export default LocationComponent

```

Alternatively, you can directly initialize the client via `createClient` with your access token and pass it to the `initSDK` function.

### Using the SDK

```typescript
import type { SidebarAppSDK } from '@contentful/app-sdk'\
import { useSDK } from '@contentful/react-apps-toolkit'
import { Form } from '@contentful/f36-components'
import { localize } from '@focus-reactive/contentful-ai-sdk'
import { useForm } from 'react-hook-form'
import { useMutation } from '@tanstack/react-query'

export default function Translate() {
const sdk = useSDK<SidebarAppSDK>()
const { handleSubmit } = useForm()

const { mutate } = useMutation({
mutationFn: localize,
})

const onSubmit = (values) => {
mutate({
targetLanguage: values.targetLanguage,
translationLevel: values.translationLevel,
entryId: sdk.entry.getSys().id,
localEntryId: values.local,
globalEntryId: values.global,
})
}

return (
<Form onSubmit={handleSubmit(onSubmit)}>
...
</Form>
)
}
```

## API

### **localize**

Translate the fields of an entry to the target language. The field values in the default locale will be used as the source. It can be used for both field-level and entry-level localizations.

#### **Parameters**

```typescript
type LocalizeFieldsProps = {
translationLevel: 'field';
targetLanguage: string;
entryId: string;
};

type LocalizeEntryProps = {
translationLevel: 'entry';
targetLanguage: string;
localEntryId: string;
globalEntryId: string;
};

type LocalizeProps = LocalizeFieldsProps | LocalizeEntryProps;
```

- `targetLanguage` - language (or locale) to which you want to translate your entry. **Available locales must be configured in space settings.**
- `translationLevel` - which localization level to use.
- `field` - add target localization to the current entry
- `entryId` - ID of the entry that you want to translate.
- `entry` - create a new entry with translated values stored in the default locale and link the global entry (container) with the newly created entry
- `localEntryId` - ID of the entry that will be used as a data source.
- `globalEntryId` - ID of the entry that will be used as a container for the newly created entry.

#### **Return**

`Promise<void>`

### **resolveEntries**

Identify global and local entries based on whether a given entry refers to any other entries or is being referenced.

#### **Parameters**

- `entryId` - ID of the entry

#### **Return**

`{ global: RecognizedEntry; local: RecognizedEntry }`, where `RecognizedEntry` is an object with the following structure:

```typescript
type RecognizedEntry = {
id: string;
name: string;
contentType: { id: string; name: string };
} | null
```

If entry is isolated (no references), `{ global: null, local: entry }` will be returned.

### **findTags**

Find the most relevant tags for the entry's content from the space's configured tags.

#### **Parameters**

- `entryId` - ID of the entry
- `contentTitle` - content title for more context

#### **Return**

An array of tags, where each tag is an object with the following structure:

```typescript
type Tag = {
id: string
title: string
}
```

### **applyTags**

Apply tags (with overwrite) to the given entry

#### **Parameters**

- `entryId` - ID of the entry
- `tags` - Array of objects with `id` property, where `id` is the ID of the tag

#### **Return**

`Promise<void>`
13 changes: 13 additions & 0 deletions packages/contentful-ai-sdk/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
50 changes: 50 additions & 0 deletions packages/contentful-ai-sdk/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"name": "@focus-reactive/contentful-ai-sdk",
"description": "Content AI SDK adapter for Contentful",
"version": "0.0.1",
"private": false,
"license": "MIT",
"type": "module",
"scripts": {
"dev:cf": "vite build --watch",
"pre-dev-contentful-sdk": "yalc add --link @focus-reactive/content-ai-sdk && yarn build && yalc publish --push",
"yalc-clean": "yalc remove --all",
"build": "tsc && vite build",
"preview": "vite preview",
"yalc-check": "yalc check"
},
"dependencies": {
"@focus-reactive/content-ai-sdk": "0.0.4",
"vite-plugin-dts": "^3.6.3",
"vite-tsconfig-paths": "^5.0.1"
},
"devDependencies": {
"@contentful/app-sdk": "^4.29.1",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"contentful-management": "10.46.4",
"eslint": "^8.45.0",
"typescript": "^5.0.2",
"vite": "^4.4.5",
"vite-plugin-dts": "^3.6.3",
"vitest": "2.1.6",
"yalc": "^1.0.0-pre.53"
},
"peerDependencies": {
"@contentful/app-sdk": "^4.29.1",
"contentful-management": "10.46.4"
},
"files": [
"dist"
],
"main": "./dist/contentful-ai-sdk.umd.js",
"module": "./dist/contentful-ai-sdk.es.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/contentful-ai-sdk.es.js",
"require": "./dist/contentful-ai-sdk.umd.js",
"types": "./dist/index.d.ts"
}
}
}
1 change: 1 addition & 0 deletions packages/contentful-ai-sdk/public/vite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions packages/contentful-ai-sdk/src/config/contentfulClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { CMAClient } from '@contentful/app-sdk';

let contentfulClient: CMAClient | null = null;

export const initContentfulClient = (client: CMAClient) => {
contentfulClient = client;
};

export const getContentfulClient = () => {
return contentfulClient;
};
13 changes: 13 additions & 0 deletions packages/contentful-ai-sdk/src/config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { initContentfulClient } from './contentfulClient';
import { initSDK as configure } from '@focus-reactive/content-ai-sdk';
import type { CMAClient } from '@contentful/app-sdk';

interface ConfigProps {
client: CMAClient;
openAiKey: string;
}

export const initSDK = (config: ConfigProps) => {
initContentfulClient(config.client);
configure({ openAiToken: config.openAiKey });
};
Loading