Skip to content

Commit

Permalink
refactor: add endpoint retrieving default card options (#1461)
Browse files Browse the repository at this point in the history
Will be used to keep all of the card options in one place.
  • Loading branch information
aalemayhu authored Apr 27, 2024
1 parent 160dabd commit eab312b
Show file tree
Hide file tree
Showing 6 changed files with 259 additions and 11 deletions.
74 changes: 74 additions & 0 deletions src/controllers/SettingsController/SettingsController.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import SettingsService, { IServiceSettings } from '../../services/SettingsService';
import SettingsController from './SettingsController';
import { SettingsInitializer } from '../../data_layer/public/Settings';

class FakeSettingsService implements IServiceSettings {
create(settings: SettingsInitializer): Promise<number[]> {
return Promise.resolve([]);
}
delete(owner: string, id: string): Promise<void> {
return Promise.resolve()
}

getById(id: string): Promise<SettingsInitializer> {
return Promise.resolve({
object_id: "1",
owner: "1",
payload: "payload"
})
}
}

describe("SettingsController", () => {

test("returns default settings for client", () => {
const settingsController = new SettingsController(new FakeSettingsService());
const defaultOptions = settingsController.getDefaultSettingsCardOptions('client');

expect(defaultOptions).toStrictEqual(
{
"add-notion-link": "false",
"use-notion-id": "true",
"all": "true",
"paragraph": "false",
"cherry": "false",
"avocado": "false",
"tags": "false",
"cloze": "true",
"enable-input": "false",
"basic-reversed": "false",
"reversed": "false",
"no-underline": "false",
"max-one-toggle-per-card": "true",
"remove-mp3-links": "true",
"perserve-newlines": "true"
}
);
})

test("returns default settings for server", () => {
const settingsController = new SettingsController(new FakeSettingsService());
const defaultOptions = settingsController.getDefaultSettingsCardOptions('server');

expect(defaultOptions).toStrictEqual(
{
'add-notion-link': 'false',
'use-notion-id': 'true',
"all": 'true',
"paragraph": 'false',
"cherry": 'false',
"avocado": 'false',
"tags": 'true',
"cloze": 'true',
'enable-input': 'false',
'basic-reversed': 'false',
"reversed": 'false',
'no-underline': 'false',
'max-one-toggle-per-card': 'true',
'perserve-newlines': 'false',
'page-emoji': 'first-emoji',
}
);
})

})
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Request, Response } from 'express';
import { sendError } from '../lib/error/sendError';
import SettingsService from '../services/SettingsService';
import { getOwner } from '../lib/User/getOwner';
import { sendError } from '../../lib/error/sendError';
import { IServiceSettings } from '../../services/SettingsService';
import { getOwner } from '../../lib/User/getOwner';
import supportedOptions, { CardOption } from './supportedOptions';
import Settings from '../../lib/parser/Settings';

class SettingsController {
constructor(private readonly service: SettingsService) {}
constructor(private readonly service: IServiceSettings) {}

async createSetting(req: Request, res: Response) {
console.info(`/settings/create ${req.params.id}`);
Expand Down Expand Up @@ -53,6 +55,23 @@ class SettingsController {
res.status(400).send();
}
}

getDefaultSettingsCardOptions(source: 'client' | 'server') {
if (source === 'client') {
return this.getDefaultOptions()
.map((option: CardOption) => {
return { [option.key]: option.value.toString() };
})
.reduce((accumulator, current) => {
return { ...accumulator, ...current };
}, {});
}
return Settings.LoadDefaultOptions();
}

getDefaultOptions() {
return supportedOptions();
}
}

export default SettingsController;
120 changes: 120 additions & 0 deletions src/controllers/SettingsController/supportedOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
export class CardOption {
key: string;

label: string;

value: boolean;

description: string;

constructor(
key: string,
label: string,
description: string,
defaultValue: boolean
) {
this.key = key;
this.label = label;
this.description = description;
this.value = defaultValue;
}
}

const supportedOptions = (): CardOption[] => {
const v = [
new CardOption(
'add-notion-link',
'Add Notion Link',
'Add a link to the Notion page where the toggle was created. Please use this with the (Use Notion ID) to avoid duplicates.',
false
),
new CardOption(
'use-notion-id',
'Use Notion ID',
'By default we create a new id from your fields. This can cause duplicates and in those cases you want to enable the Notion ID which is more reliable and avoid duplicates.',
true
),
new CardOption(
'all',
'Use All Toggle Lists',
'By default we only check for toggle lists in the first page. Use this option to retreive toggle lists from anywhere in the page.',
true
),
new CardOption(
'paragraph',
'Use Plain Text for Back',
'This option will remove formatting and get the text content only.',
false
),
new CardOption(
'cherry',
'Enable Cherry Picking Using 🍒 Emoji',
'This will Only create flashcards from the toggle lists that include 🍒 in the toggle (header or body)',
false
),
new CardOption(
'avocado',
"Only Create Flashcards From Toggles That Don't Have The 🥑 Emoji",
"This option enables you to ignore certain toggles when creating flashcards from pages that you don't want to change too much.",
false
),
new CardOption(
'tags',
'Treat Strikethrough as Tags',
'This will go treat the strikethroughs in the page as global ones. The ones inside of a toggle will be treated as locally to the toggle.',
false
),
new CardOption(
'cloze',
'Cloze Deletion',
'Create cloze flashcards from code blocks.',
true
),
new CardOption(
'enable-input',
'Treat Bold Text as Input',
'Words marked as bold will be removed and you will have to enter them in when reviewing the card. This is useful when you need to type out the answer.',
false
),
new CardOption(
'basic-reversed',
'Basic and Reversed',
'Create the question and answer flashcards but also reversed ones. Where the answer and question change places.',
false
),
new CardOption(
'reversed',
'Just the Reversed Flashcards',
'Only create flashcards from the reverse. This is useful when you want to say show an image first.',
false
),
new CardOption(
'no-underline',
'Remove Underlines',
'Disable underline. This is an option that was created due to changes in the way Notion handles underlines.',
false
),
new CardOption(
'max-one-toggle-per-card',
'Maximum One Toggle Per Card',
"This will limit to 1 card so you don't see too many toggles in one card. When you combine this with 'Use all toggle lists' you can create flashcards from everything in your upload, regardless of how deeply nested they are.",
true
),
new CardOption(
'remove-mp3-links',
'Remove the MP3 Links Created From Audio Files',
"Due to backwards-compatability we leave links untouched but this option let's you remove mp3 links",
true
),
new CardOption(
'perserve-newlines',
'Preserve Newlines in the Toggle Header and Body',
'This will allow you to use SHIFT-Enter in the toggles to create multiple lines for all card types (Basic, Cloze, etc.)',
true
),
];

return v.filter(Boolean);
};

export default supportedOptions;
4 changes: 4 additions & 0 deletions src/lib/parser/Settings/Settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ export class Settings {
}
}

/*
* The default options for Notion integration differ with the ones in the HTML form.
* To avoid regressions we have to keep the same defaults until a proper migration can be done.
*/
static LoadDefaultOptions(): { [key: string]: string } {
return {
'add-notion-link': 'false',
Expand Down
11 changes: 10 additions & 1 deletion src/routes/SettingsRouter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import express from 'express';

import RequireAuthentication from './middleware/RequireAuthentication';
import SettingsController from '../controllers/SettingsController';
import SettingsController from '../controllers/SettingsController/SettingsController';
import SettingsRepository from '../data_layer/SettingsRepository';
import { getDatabase } from '../data_layer';
import SettingsService from '../services/SettingsService';
Expand All @@ -22,6 +22,15 @@ const SettingsRouter = () => {
router.get('/api/settings/find/:id', RequireAuthentication, (req, res) =>
controller.findSetting(req, res)
);
router.get('/api/settings/default', (_req, res) => {
const defaultOptions = controller.getDefaultSettingsCardOptions('client');
res.json(defaultOptions);
});

router.get('/api/settings/card-options', (_req, res) => {
const defaultOptions = controller.getDefaultOptions();
res.json(defaultOptions);
});

return router;
};
Expand Down
34 changes: 28 additions & 6 deletions src/services/SettingsService.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,41 @@
import SettingsRepository from '../data_layer/SettingsRepository';
import { SettingsInitializer } from '../data_layer/public/Settings';

class SettingsService {
export interface IServiceSettings {
create: (settings: SettingsInitializer) => Promise<number[]>;
delete: (owner: string, id: string) => Promise<void>;
getById: (id: string) => Promise<SettingsInitializer>;
}

class SettingsService implements IServiceSettings {
constructor(private readonly repository: SettingsRepository) {}

create({ owner, payload, object_id }: SettingsInitializer) {
return this.repository.create({
owner,
payload,
object_id,
return new Promise<number[]>((resolve, reject) => {
return this.repository
.create({
owner,
payload,
object_id,
})
.then((settings) => {
resolve(settings);
})
.catch((error) => {
reject(error);
});
});
}

delete(owner: string, id: string) {
return this.repository.delete(owner, id);
return new Promise<void>(async (resolve, reject) => {
try {
await this.repository.delete(owner, id);
resolve();
} catch (error) {
reject(error);
}
});
}

getById(id: string) {
Expand Down

0 comments on commit eab312b

Please sign in to comment.