Skip to content

Commit

Permalink
feature: moved to new eslint v9 config
Browse files Browse the repository at this point in the history
  • Loading branch information
JustSamuel committed Nov 8, 2024
1 parent 4088237 commit 631a621
Show file tree
Hide file tree
Showing 10 changed files with 751 additions and 350 deletions.
6 changes: 0 additions & 6 deletions .eslintrc.cjs

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/lint-and-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ jobs:
node-version: "22.x"
package-manager: "yarn"
lint: true
format: false
format: true
build: true
3 changes: 3 additions & 0 deletions .lintstagedrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"./src/**/*.{ts}": ["yarn format", "yarn lint"]
}
8 changes: 8 additions & 0 deletions .prettierrc.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import prettier from '@gewis/eslint-config/prettier.mjs';

/**
* @type {import("prettier").Config}
*/
export default {
...prettier,
};
14 changes: 14 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import eslint from '@gewis/eslint-config/eslint.common.mjs';
import prettier from '@gewis/eslint-config/eslint.prettier.mjs';

export default [
...eslint,
{
settings: {
react: {
version: 'detect',
},
},
},
...prettier,
];
13 changes: 8 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,24 @@
"start": "ts-node src/index.ts",
"start:env": "ts-node -r dotenv/config src/index.ts",
"build": "tsc",
"lint": "eslint . --ext .vue,.js,.jsx,.mjs,.ts,.tsx,.cts,.mts,.json --ignore-path .gitignore",
"lint:fix": "eslint . --fix --ext .vue,.js,.jsx,.mjs,.ts,.tsx,.cts,.mts,.json --ignore-path .gitignore"
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"format": "prettier --ignore-path .gitignore --check ./src/",
"format:fix": "prettier --ignore-path .gitignore --write ./src/"
},
"dependencies": {
"@gewis/planka-client": "github:GEWIS/planka-client#3bb88c2a8d86a9ba8f4fccf0a70e534047c6f88c",
"@gewis/planka-client": "github:GEWIS/planka-client#v1.1.0",
"@hey-api/client-fetch": "^0.4.2",
"imapflow": "^1.0.164"
},
"devDependencies": {
"@gewis/eslint-config": "https://github.com/GEWIS/eslint-config",
"@gewis/eslint-config": "https://github.com/GEWIS/eslint-config#v1.2.0",
"@types/imapflow": "^1.0.19",
"@types/node": "^22.7.9",
"dotenv": "^16.0.0",
"ts-node": "^10.9.2",
"typescript": "^5.2.2"
"typescript": "^5.2.2",
"prettier": "^3.3.3"
},
"repository": {
"type": "git",
Expand Down
8 changes: 4 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Mailer from "./mailer";
import Planka from "./planka";
import Mailer from './mailer';
import Planka from './planka';

// Main workflow
async function main() {
Expand All @@ -10,9 +10,9 @@ async function main() {
const emails = await mailer.handleEmails();
const result = await Planka.processCards(emails);
await mailer.handleResults(result);
console.log("Process completed successfully.");
console.log('Process completed successfully.');

Check warning on line 13 in src/index.ts

View workflow job for this annotation

GitHub Actions / build-and-lint / build-and-lint-yarn

Unexpected console statement
} catch (error) {
console.error("An error occurred during the process:", error);
console.error('An error occurred during the process:', error);
}
}

Expand Down
47 changes: 20 additions & 27 deletions src/mailer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ImapFlow, Readable } from "imapflow";
import { ImapFlow, Readable } from 'imapflow';

export interface CardEmail {
title: string;
Expand Down Expand Up @@ -55,7 +55,7 @@ function extractDate(subject: string): Date | null {
* @returns {Promise<string>} The content of the stream as a string.
*/
const readableToString = async (readable: Readable): Promise<string> => {
let result = "";
let result = '';

for await (const chunk of readable) {
result += chunk;
Expand All @@ -70,26 +70,23 @@ const readableToString = async (readable: Readable): Promise<string> => {
* @param {string} messageId - The ID of the email message.
* @returns {Promise<string>} The email body as a string.
*/
const downloadEmailBody = async (
client: ImapFlow,
messageId: string,
): Promise<string> => {
const { content } = await client.download(messageId, "1", { uid: true }); // part '1' is typically the plain text part
const downloadEmailBody = async (client: ImapFlow, messageId: string): Promise<string> => {
const { content } = await client.download(messageId, '1', { uid: true }); // part '1' is typically the plain text part
return readableToString(content);
};

export default class Mailer {
private readonly client: ImapFlow;
private ROOT_PATH: string = process.env["IMAP_ROOT"] || "API";
private ROOT_PATH: string = process.env['IMAP_ROOT'] || 'API';

constructor() {
this.client = new ImapFlow({
host: process.env["IMAP_HOST"] || "localhost",
port: Number(process.env["IMAP_PORT"] || "993"),
host: process.env['IMAP_HOST'] || 'localhost',
port: Number(process.env['IMAP_PORT'] || '993'),
secure: true,
auth: {
user: process.env["IMAP_USERNAME"] || "user",
pass: process.env["IMAP_PASSWORD"],
user: process.env['IMAP_USERNAME'] || 'user',
pass: process.env['IMAP_PASSWORD'],
},
});
}
Expand All @@ -105,14 +102,12 @@ export default class Mailer {
const emails: CardEmail[] = [];

try {
for await (
const message of this.client.fetch("1:*", {
envelope: true,
headers: true,
flags: true,
})
) {
const headers = "" + message.headers;
for await (const message of this.client.fetch('1:*', {
envelope: true,
headers: true,
flags: true,
})) {
const headers = '' + message.headers;
const plankaBoardId = extractPlankaBoardId(headers);
const plankaListId = extractPlankaListId(headers);
const date = extractDate(message.envelope.subject);
Expand Down Expand Up @@ -143,7 +138,7 @@ export default class Mailer {

if (rejected.length !== 0) {
await this.client.mailboxOpen(`${this.ROOT_PATH}/IN`);
await this.rejectEmail(rejected.join(",")).catch((e) => console.error(e));
await this.rejectEmail(rejected.join(',')).catch((e) => console.error(e));
await this.client.mailboxClose();
}

Expand All @@ -154,16 +149,14 @@ export default class Mailer {
* Handles the results of card processing by accepting or rejecting emails.
* @param {Array<{card: CardEmail, state: 'ACCEPTED' | 'REJECTED'}>} results - List of cards and their states.
*/
async handleResults(
results: { card: CardEmail; state: "ACCEPTED" | "REJECTED" }[],
) {
async handleResults(results: { card: CardEmail; state: 'ACCEPTED' | 'REJECTED' }[]) {
await this.client.mailboxOpen(`${this.ROOT_PATH}/IN`);
for (const result of results) {
if (result.state === "ACCEPTED") {
console.log("accepted", result.card.uid);
if (result.state === 'ACCEPTED') {
console.log('accepted', result.card.uid);

Check warning on line 156 in src/mailer.ts

View workflow job for this annotation

GitHub Actions / build-and-lint / build-and-lint-yarn

Unexpected console statement
await this.acceptEmail(result.card.uid);
} else {
console.log("rejected", result.card.uid);
console.log('rejected', result.card.uid);

Check warning on line 159 in src/mailer.ts

View workflow job for this annotation

GitHub Actions / build-and-lint / build-and-lint-yarn

Unexpected console statement
await this.rejectEmail(result.card.uid);
}
}
Expand Down
49 changes: 19 additions & 30 deletions src/planka.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import {
CreateCardRequest,
UpdateCardRequest,
getBoard,
updateCard, GetBoardRequest, GetBoardResponse
} from "@gewis/planka-client";
import type { List } from "@gewis/planka-client";
import type { Client, Options } from "@hey-api/client-fetch";
import type { CardEmail } from "./mailer.ts";
updateCard,
GetBoardRequest,
GetBoardResponse,
} from '@gewis/planka-client';
import type { List } from '@gewis/planka-client';
import type { Client, Options } from '@hey-api/client-fetch';
import type { CardEmail } from './mailer.ts';

const DEFAULT_PLANKA_URL = process.env["PLANKA_URL"] ||
"http://localhost:3000";
const DEFAULT_PLANKA_URL = process.env['PLANKA_URL'] || 'http://localhost:3000';

interface CacheEntry {
board: GetBoardResponse;
Expand All @@ -37,8 +38,7 @@ export default class Planka {
private initializeClient() {
if (!Planka.client) {
const plankaUrl = this.settings.plankaUrl || DEFAULT_PLANKA_URL;
const plankaApiKey = this.settings.plankaApiKey ||
process.env["PLANKA_API_KEY"];
const plankaApiKey = this.settings.plankaApiKey || process.env['PLANKA_API_KEY'];

client.setConfig({
baseUrl: plankaUrl,
Expand All @@ -55,9 +55,7 @@ export default class Planka {
* @param {Object} [settings] - Optional settings for Planka initialization.
* @returns {Planka} Returns the singleton instance of Planka.
*/
public static initialize(
settings?: { plankaUrl?: string; plankaApiKey?: string },
): Planka {
public static initialize(settings?: { plankaUrl?: string; plankaApiKey?: string }): Planka {
if (!Planka.instance) {
Planka.instance = new Planka(settings || {});
}
Expand All @@ -70,9 +68,7 @@ export default class Planka {
*/
private static pre() {
if (!Planka.client) {
throw new Error(
"Client has not been initialized. Please call initialize() first.",
);
throw new Error('Client has not been initialized. Please call initialize() first.');
}
}

Expand Down Expand Up @@ -107,16 +103,13 @@ export default class Planka {
const status = board.response.status;

if (status === 200 && board.data) {

// Find the preferred list, which is the list named 'mail' or the first list available
let preferredList = null;
const lists: List[] = (board.data?.included as any)?.lists ?? [];
const lists: List[] = (board.data?.included as { lists: List[] })?.lists ?? [];

if (lists.length > 0) {
// Find the list named 'mail', or fall back to the first list if not found
preferredList = lists.find((list: List) =>
list.name.toLowerCase() === "mail"
) || lists[0];
preferredList = lists.find((list: List) => list.name.toLowerCase() === 'mail') || lists[0];
}

Planka.boardCache.set(id, { board: board.data, preferredList });
Expand All @@ -133,31 +126,27 @@ export default class Planka {
* @returns {Promise<{result: {card: CardEmail, state: 'ACCEPTED' | 'REJECTED'}[]}>}
* Returns a result object indicating whether each card was accepted or rejected.
*/
static async processCards(
cards: CardEmail[],
): Promise<
{ card: CardEmail; state: "ACCEPTED" | "REJECTED" }[]
> {
static async processCards(cards: CardEmail[]): Promise<{ card: CardEmail; state: 'ACCEPTED' | 'REJECTED' }[]> {
Planka.pre();

// Ensure boards are cached before card processing
await Planka.preProcessCards(cards);

const results: { card: CardEmail; state: "ACCEPTED" | "REJECTED" }[] = [];
const results: { card: CardEmail; state: 'ACCEPTED' | 'REJECTED' }[] = [];

for (const card of cards) {
const board = Planka.boardCache.get(card.boardId);

if (!board) {
// Reject card if the board is not found in the cache
results.push({ card, state: "REJECTED" });
results.push({ card, state: 'REJECTED' });
continue;
}

const listId = card.listId || board.preferredList?.id;
if (!listId) {
// Reject card if the board has no list
results.push({ card, state: "REJECTED" });
results.push({ card, state: 'REJECTED' });
continue;
}

Expand All @@ -169,14 +158,14 @@ export default class Planka {
body: {
name: card.title,
position: 0,
}
},
} as Options<CreateCardRequest, false>).then(async (result) => {
const cardResult = result.data;
const status = result.response.status;

if (status !== 200 || !cardResult) return;

results.push({ card, state: "ACCEPTED" });
results.push({ card, state: 'ACCEPTED' });

// Update the card's description and due date if provided
if (card.body) {
Expand Down
Loading

0 comments on commit 631a621

Please sign in to comment.