From 19fd3ddd0e46fac4f1ba1c7e1e159c739970165e Mon Sep 17 00:00:00 2001 From: Rickard Natt och Dag Date: Tue, 30 Mar 2021 17:21:18 +0200 Subject: [PATCH] chore: add prettier (#101) --- .eslintrc.js | 9 +- .prettierrc | 10 + agentFetchWrapper.js | 34 +- devrun.js | 13 +- jest.config.js | 4 +- .../@react-native-cookies/cookies.ts | 20 +- lib/api.test.ts | 8 +- lib/api.ts | 68 ++- lib/cookies.test.ts | 61 ++- lib/cookies.ts | 61 ++- lib/fakeData.ts | 136 +++--- lib/fetcher.ts | 23 +- lib/index.ts | 5 +- lib/loginStatus.ts | 20 +- lib/parse.test.ts | 443 ++++++++++-------- lib/parse.ts | 140 ++++-- lib/parseHtml.test.ts | 3 +- lib/parseHtml.ts | 38 +- lib/routes.ts | 57 +-- lib/types.ts | 18 +- package.json | 3 + run.js | 30 +- yarn.lock | 29 ++ 23 files changed, 734 insertions(+), 499 deletions(-) create mode 100644 .prettierrc diff --git a/.eslintrc.js b/.eslintrc.js index a24addd..26eb2c9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -3,18 +3,17 @@ module.exports = { parserOptions: { ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features sourceType: 'module', // Allows for the use of imports - project: ['./tsconfig.eslint.json'] + project: ['./tsconfig.eslint.json'], }, - extends: [ - 'airbnb-typescript/base', - ], + extends: ['airbnb-typescript/base', 'prettier'], + plugins: ['prettier'], ignorePatterns: ['*.test.ts'], rules: { // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs // e.g. "@typescript-eslint/explicit-function-return-type": "off", // '@typescript-eslint/indent': ['error', 2], '@typescript-eslint/semi': [2, 'never'], - 'max-len': ['error', { code: 120, 'ignoreUrls': true }], + 'max-len': ['error', { code: 120, ignoreUrls: true }], 'import/prefer-default-export': 0, }, } diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..da36fd1 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,10 @@ +{ + "printWidth": 80, + "tabWidth": 2, + "useTabs": false, + "semi": false, + "singleQuote": true, + "trailingComma": "es5", + "bracketSpacing": true, + "jsxBracketSameLine": false +} diff --git a/agentFetchWrapper.js b/agentFetchWrapper.js index f251721..f269856 100644 --- a/agentFetchWrapper.js +++ b/agentFetchWrapper.js @@ -1,17 +1,17 @@ -module.exports = function agentDecorator (fetch, agent) { - fetch = fetch || window.fetch - - async function fetchWrapper (url, opts) { - opts = opts || {} - - // Prepare request - opts.agent = agent - - // Actual request - const res = await fetch(url, opts) - - return res - } - - return fetchWrapper - } \ No newline at end of file +module.exports = function agentDecorator(fetch, agent) { + fetch = fetch || window.fetch + + async function fetchWrapper(url, opts) { + opts = opts || {} + + // Prepare request + opts.agent = agent + + // Actual request + const res = await fetch(url, opts) + + return res + } + + return fetchWrapper +} diff --git a/devrun.js b/devrun.js index aad935a..f6d45ed 100644 --- a/devrun.js +++ b/devrun.js @@ -38,8 +38,13 @@ async function run() { if (bankIdUsed) { const sessionCookie = getSessionCookieFromCookieJar() ensureDirectoryExistence('./record') - await writeFile('./record/latestSessionCookie.txt', JSON.stringify(sessionCookie)) - console.log('Session cookie saved to file ./record/latesSessionCookie.txt') + await writeFile( + './record/latestSessionCookie.txt', + JSON.stringify(sessionCookie) + ) + console.log( + 'Session cookie saved to file ./record/latesSessionCookie.txt' + ) } console.log('user') const user = await api.getUser() @@ -116,7 +121,9 @@ async function Login(api) { if (useBankId) { console.log('*** BankId login - open BankId app ***') if (!personalNumber) { - console.error('You must pass in a valid personal number, eg `node run 197001011111`') + console.error( + 'You must pass in a valid personal number, eg `node run 197001011111`' + ) process.exit(1) } const status = await api.login(personalNumber) diff --git a/jest.config.js b/jest.config.js index 1e03f3b..612ab1c 100644 --- a/jest.config.js +++ b/jest.config.js @@ -2,8 +2,8 @@ module.exports = { preset: 'ts-jest', testEnvironment: 'jsdom', transform: { - '.(ts|tsx)': 'ts-jest' + '.(ts|tsx)': 'ts-jest', }, testRegex: '(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$', - moduleFileExtensions: ["ts", "tsx", "js"] + moduleFileExtensions: ['ts', 'tsx', 'js'], } diff --git a/lib/__mocks__/@react-native-cookies/cookies.ts b/lib/__mocks__/@react-native-cookies/cookies.ts index 68efcca..7ce777b 100644 --- a/lib/__mocks__/@react-native-cookies/cookies.ts +++ b/lib/__mocks__/@react-native-cookies/cookies.ts @@ -32,13 +32,14 @@ const convertTtoC = (cookie: string | TCookie): Cookie => { name: cookie.key, value: cookie.value, domain: cookie.domain || undefined, - expires: cookie.expires === 'Infinity' ? undefined : cookie.expires.toUTCString(), + expires: + cookie.expires === 'Infinity' ? undefined : cookie.expires.toUTCString(), httpOnly: cookie.httpOnly || undefined, path: cookie.path || undefined, secure: cookie.secure, } } -const convertCtoT = (cookie: Cookie): TCookie => ( +const convertCtoT = (cookie: Cookie): TCookie => new TCookie({ key: cookie.name, value: cookie.value, @@ -48,13 +49,14 @@ const convertCtoT = (cookie: Cookie): TCookie => ( path: cookie.path, secure: cookie.secure || false, }) -) -const convertCookies = (cookies: TCookie[]): Cookies => ( - cookies.reduce((map, cookie) => ({ - ...map, - [cookie.key]: convertTtoC(cookie), - }), {} as Cookies) -) +const convertCookies = (cookies: TCookie[]): Cookies => + cookies.reduce( + (map, cookie) => ({ + ...map, + [cookie.key]: convertTtoC(cookie), + }), + {} as Cookies + ) const jar = new CookieJar() const CookieManager: CookieManagerStatic = { diff --git a/lib/api.test.ts b/lib/api.test.ts index 7ee46d2..1de34c7 100644 --- a/lib/api.test.ts +++ b/lib/api.test.ts @@ -101,17 +101,17 @@ describe('api', () => { it('throws error on external api error', async () => { expect.hasAssertions() - const data = "" + const data = '' response.json.mockResolvedValue(data) response.ok = false response.status = 500 - response.statusText = "Internal Server Error" + response.statusText = 'Internal Server Error' const personalNumber = 'my personal number' try { await api.login(personalNumber) } catch (error) { - expect(error.message).toEqual(expect.stringContaining("Server Error")) + expect(error.message).toEqual(expect.stringContaining('Server Error')) } }) }) @@ -170,7 +170,7 @@ describe('api', () => { const user = await api.getUser() expect(user).toEqual({ firstName: 'Namn', - lastName: 'Namnsson' + lastName: 'Namnsson', }) const children = await api.getChildren() diff --git a/lib/api.ts b/lib/api.ts index 4043102..07cb531 100644 --- a/lib/api.ts +++ b/lib/api.ts @@ -2,10 +2,7 @@ import { DateTime } from 'luxon' import { EventEmitter } from 'events' import { decode } from 'he' import * as html from 'node-html-parser' -import { - checkStatus, - LoginStatusChecker, -} from './loginStatus' +import { checkStatus, LoginStatusChecker } from './loginStatus' import { AuthTicket, CalendarItem, @@ -25,9 +22,8 @@ import * as parse from './parse' import wrap, { Fetcher, FetcherOptions } from './fetcher' import * as fake from './fakeData' -const fakeResponse = (data: T): Promise => new Promise((res) => ( - setTimeout(() => res(data), 200 + Math.random() * 800) -)) +const fakeResponse = (data: T): Promise => + new Promise((res) => setTimeout(() => res(data), 200 + Math.random() * 800)) export class Api extends EventEmitter { private fetch: Fetcher @@ -44,7 +40,11 @@ export class Api extends EventEmitter { public childControllerUrl?: string - constructor(fetch: Fetch, cookieManager: CookieManager, options?: FetcherOptions) { + constructor( + fetch: Fetch, + cookieManager: CookieManager, + options?: FetcherOptions + ) { super() this.fetch = wrap(fetch, options) this.cookieManager = cookieManager @@ -65,7 +65,10 @@ export class Api extends EventEmitter { } } - public async getSession(url: string, options?: RequestInit): Promise { + public async getSession( + url: string, + options?: RequestInit + ): Promise { const init = this.getRequestInit(options) const cookie = await this.cookieManager.getCookieString(url) return { @@ -95,7 +98,9 @@ export class Api extends EventEmitter { const ticketResponse = await this.fetch('auth-ticket', ticketUrl) if (!ticketResponse.ok) { - throw new Error(`Server Error [${ticketResponse.status}] [${ticketResponse.statusText}] [${ticketUrl}]`) + throw new Error( + `Server Error [${ticketResponse.status}] [${ticketResponse.statusText}] [${ticketUrl}]` + ) } const ticket: AuthTicket = await ticketResponse.json() @@ -112,12 +117,14 @@ export class Api extends EventEmitter { this.isLoggedIn = true this.emit('login') }) - status.on('ERROR', () => { this.personalNumber = undefined }) + status.on('ERROR', () => { + this.personalNumber = undefined + }) return status } - public async setSessionCookie(sessionCookie : string) : Promise { + public async setSessionCookie(sessionCookie: string): Promise { // Manually set cookie in this call and let the cookieManager // handle it from here // If we put it into the cookieManager manually, we get duplicate cookies @@ -152,13 +159,17 @@ export class Api extends EventEmitter { const response = await this.fetch('hemPage', url, session) const text = await response.text() const doc = html.parse(decode(text)) - const xsrfToken = doc.querySelector('input[name="__RequestVerificationToken"]')?.getAttribute('value') || '' + const xsrfToken = + doc + .querySelector('input[name="__RequestVerificationToken"]') + ?.getAttribute('value') || '' const scriptTags = doc.querySelectorAll('script') const childControllerScriptTag = scriptTags.find((elem) => { const srcAttr = elem.getAttribute('src') return srcAttr?.startsWith('/vardnadshavare/bundles/childcontroller') }) - this.childControllerUrl = routes.baseEtjanst + childControllerScriptTag?.getAttribute('src') + this.childControllerUrl = + routes.baseEtjanst + childControllerScriptTag?.getAttribute('src') this.addHeader('x-xsrf-token', xsrfToken) } @@ -170,7 +181,8 @@ export class Api extends EventEmitter { const apiKeyRegex = /"API-Key": "([\w\d]+)"/gm const apiKeyMatches = apiKeyRegex.exec(text) - const apiKey = apiKeyMatches && apiKeyMatches.length > 1 ? apiKeyMatches[1] : '' + const apiKey = + apiKeyMatches && apiKeyMatches.length > 1 ? apiKeyMatches[1] : '' this.addHeader('API-Key', apiKey) } @@ -192,12 +204,18 @@ export class Api extends EventEmitter { } private async retrieveCreateItemHeaders() { - const response = await this.fetch('createItemConfig', routes.createItemConfig) + const response = await this.fetch( + 'createItemConfig', + routes.createItemConfig + ) const json = await response.json() return json } - private async retrieveAuthToken(url: string, authBody: string): Promise { + private async retrieveAuthToken( + url: string, + authBody: string + ): Promise { const session = this.getRequestInit({ method: 'POST', headers: { @@ -231,7 +249,9 @@ export class Api extends EventEmitter { }) if (!response.ok) { - throw new Error(`Server Error [${response.status}] [${response.statusText}] [${url}]`) + throw new Error( + `Server Error [${response.status}] [${response.statusText}] [${url}]` + ) } const authData = await response.json() @@ -280,7 +300,9 @@ export class Api extends EventEmitter { const response = await this.fetch('children', url, session) if (!response.ok) { - throw new Error(`Server Error [${response.status}] [${response.statusText}] [${url}]`) + throw new Error( + `Server Error [${response.status}] [${response.statusText}] [${url}]` + ) } const data = await response.json() @@ -307,7 +329,11 @@ export class Api extends EventEmitter { return parse.classmates(data) } - public async getSchedule(child: Child, from: DateTime, to: DateTime): Promise { + public async getSchedule( + child: Child, + from: DateTime, + to: DateTime + ): Promise { if (this.isFake) return fakeResponse(fake.schedule(child)) const url = routes.schedule(child.sdsId, from.toISODate(), to.toISODate()) @@ -357,7 +383,7 @@ export class Api extends EventEmitter { return parse.menuList(data) } - private async getMenuChoice(child : Child) : Promise { + private async getMenuChoice(child: Child): Promise { const url = routes.menuChoice(child.id) const session = this.getRequestInit() const response = await this.fetch('menu-choice', url, session) diff --git a/lib/cookies.test.ts b/lib/cookies.test.ts index 4937d48..1a519a1 100644 --- a/lib/cookies.test.ts +++ b/lib/cookies.test.ts @@ -1,5 +1,10 @@ -import { deserialize, serialize, wrapToughCookie, wrapReactNativeCookieManager } from './cookies' -import { Cookie, CookieManager} from './types' +import { + deserialize, + serialize, + wrapToughCookie, + wrapReactNativeCookieManager, +} from './cookies' +import { Cookie, CookieManager } from './types' import { CookieJar } from 'tough-cookie' import RNCookieManager from '@react-native-cookies/cookies' @@ -75,7 +80,8 @@ describe('CookieManager', () => { expect(deserialize(cookieStr)).toEqual(cookie) }) it('deserializes cookies with all properties', () => { - const cookieStr = 'foo=bar; Expires=Tue, 09 Mar 2021 08:27:48 GMT; Domain=.stockholm.se; Path=/; Secure; HTTPOnly' + const cookieStr = + 'foo=bar; Expires=Tue, 09 Mar 2021 08:27:48 GMT; Domain=.stockholm.se; Path=/; Secure; HTTPOnly' const cookie: Cookie = { name: 'foo', value: 'bar', @@ -150,7 +156,8 @@ describe('CookieManager', () => { expect(serialize(cookie)).toEqual(cookieStr) }) it('serializes cookies with all properties', () => { - const cookieStr = 'foo=bar; Expires=Tue, 09 Mar 2021 08:27:48 GMT; Domain=.stockholm.se; Path=/; Secure; HttpOnly' + const cookieStr = + 'foo=bar; Expires=Tue, 09 Mar 2021 08:27:48 GMT; Domain=.stockholm.se; Path=/; Secure; HttpOnly' const cookie: Cookie = { name: 'foo', value: 'bar', @@ -174,15 +181,19 @@ describe('CookieManager', () => { }) it('handles getCookieString', async () => { const url = 'https://etjanster.stockholm.se/' - const cookieStr = 'foo=bar; Domain=.stockholm.se; Path=/; Secure; HttpOnly' + const cookieStr = + 'foo=bar; Domain=.stockholm.se; Path=/; Secure; HttpOnly' await jar.setCookie(cookieStr, url) - const storedCookies = await manager.getCookieString('https://foo.stockholm.se/bar/baz') + const storedCookies = await manager.getCookieString( + 'https://foo.stockholm.se/bar/baz' + ) expect(storedCookies).toEqual('foo=bar') }) it('handles getCookies', async () => { const url = 'https://etjanster.stockholm.se/' - const cookieStr = 'foo=bar; Domain=.stockholm.se; Path=/; Secure; HttpOnly' + const cookieStr = + 'foo=bar; Domain=.stockholm.se; Path=/; Secure; HttpOnly' const cookie: Cookie = { name: 'foo', value: 'bar', @@ -193,8 +204,10 @@ describe('CookieManager', () => { } await jar.setCookie(cookieStr, url) - const storedCookies = await manager.getCookies('https://foo.stockholm.se/bar/baz') - + const storedCookies = await manager.getCookies( + 'https://foo.stockholm.se/bar/baz' + ) + expect(storedCookies).toHaveLength(1) expect(storedCookies[0]).toEqual(cookie) }) @@ -216,7 +229,8 @@ describe('CookieManager', () => { }) it('handles setCookieString', async () => { const url = 'https://etjanster.stockholm.se/' - const cookieStr = 'foo=bar; Domain=.stockholm.se; Path=/; Secure; HttpOnly' + const cookieStr = + 'foo=bar; Domain=.stockholm.se; Path=/; Secure; HttpOnly' await manager.setCookieString(cookieStr, url) @@ -225,12 +239,13 @@ describe('CookieManager', () => { }) it('handles clearAll', async () => { const url = 'https://etjanster.stockholm.se/' - const cookieStr = 'foo=bar; Domain=.stockholm.se; Path=/; Secure; HttpOnly' + const cookieStr = + 'foo=bar; Domain=.stockholm.se; Path=/; Secure; HttpOnly' await manager.setCookieString(cookieStr, url) await manager.clearAll() const cookies = await jar.getCookieString(url) - + expect(cookies).toEqual('') }) }) @@ -242,15 +257,19 @@ describe('CookieManager', () => { }) it('handles getCookieString', async () => { const url = 'https://etjanster.stockholm.se/' - const cookieStr = 'foo=bar; Domain=.stockholm.se; Path=/; Secure; HttpOnly' + const cookieStr = + 'foo=bar; Domain=.stockholm.se; Path=/; Secure; HttpOnly' await RNCookieManager.setFromResponse(url, cookieStr) - const storedCookies = await manager.getCookieString('https://foo.stockholm.se/bar/baz') + const storedCookies = await manager.getCookieString( + 'https://foo.stockholm.se/bar/baz' + ) expect(storedCookies).toEqual('foo=bar') }) it('handles getCookies', async () => { const url = 'https://etjanster.stockholm.se/' - const cookieStr = 'foo=bar; Domain=.stockholm.se; Path=/; Secure; HttpOnly' + const cookieStr = + 'foo=bar; Domain=.stockholm.se; Path=/; Secure; HttpOnly' const cookie: Cookie = { name: 'foo', value: 'bar', @@ -261,8 +280,10 @@ describe('CookieManager', () => { } await RNCookieManager.setFromResponse(url, cookieStr) - const storedCookies = await manager.getCookies('https://foo.stockholm.se/bar/baz') - + const storedCookies = await manager.getCookies( + 'https://foo.stockholm.se/bar/baz' + ) + expect(storedCookies).toHaveLength(1) expect(storedCookies[0]).toEqual(cookie) }) @@ -285,7 +306,8 @@ describe('CookieManager', () => { }) it('handles setCookieString', async () => { const url = 'https://etjanster.stockholm.se/' - const cookieStr = 'foo=bar; Domain=.stockholm.se; Path=/; Secure; HttpOnly' + const cookieStr = + 'foo=bar; Domain=.stockholm.se; Path=/; Secure; HttpOnly' await manager.setCookieString(cookieStr, url) const cookies = await RNCookieManager.get(url) @@ -295,7 +317,8 @@ describe('CookieManager', () => { }) it('handles clearAll', async () => { const url = 'https://etjanster.stockholm.se/' - const cookieStr = 'foo=bar; Domain=.stockholm.se; Path=/; Secure; HttpOnly' + const cookieStr = + 'foo=bar; Domain=.stockholm.se; Path=/; Secure; HttpOnly' await manager.setCookieString(cookieStr, url) await manager.clearAll() diff --git a/lib/cookies.ts b/lib/cookies.ts index 0854513..cb8bf2d 100644 --- a/lib/cookies.ts +++ b/lib/cookies.ts @@ -2,7 +2,7 @@ import { camelCase, pascalCase } from 'change-case' import { Cookie, CookieManager } from './types' interface IndexableCookie extends Cookie { - [key: string]: string|boolean|undefined + [key: string]: string | boolean | undefined } interface Serializer { (cookie: Cookie): string @@ -15,19 +15,25 @@ export const serialize: Serializer = (cookie) => { const tokens = [`${ic.name}=${ic.value}`] const keyVals = ['expires', 'domain', 'path'] - keyVals.filter((key) => ic[key]).forEach((key) => { - tokens.push(`${pascalCase(key)}=${ic[key]}`) - }) + keyVals + .filter((key) => ic[key]) + .forEach((key) => { + tokens.push(`${pascalCase(key)}=${ic[key]}`) + }) const bools = ['secure', 'httpOnly'] - bools.filter((key) => ic[key]).forEach((key) => { - tokens.push(pascalCase(key)) - }) + bools + .filter((key) => ic[key]) + .forEach((key) => { + tokens.push(pascalCase(key)) + }) return tokens.join('; ') } export const deserialize: Deserializer = (cookieString) => { - const [nameVal, ...others] = cookieString.split(';').map((token) => token.trim()) + const [nameVal, ...others] = cookieString + .split(';') + .map((token) => token.trim()) const [name, value] = nameVal.split('=') const cookie: Cookie = { @@ -35,21 +41,23 @@ export const deserialize: Deserializer = (cookieString) => { value, } - others.map((keyVal) => keyVal.split('=')).forEach(([key, val]) => { - const prop = camelCase(key) - // eslint-disable-next-line default-case - switch (prop) { - case 'expires': - case 'domain': - case 'path': - cookie[prop] = val - break - case 'secure': - case 'httpOnly': - cookie[prop] = true - break - } - }) + others + .map((keyVal) => keyVal.split('=')) + .forEach(([key, val]) => { + const prop = camelCase(key) + // eslint-disable-next-line default-case + switch (prop) { + case 'expires': + case 'domain': + case 'path': + cookie[prop] = val + break + case 'secure': + case 'httpOnly': + cookie[prop] = true + break + } + }) return cookie } @@ -87,12 +95,15 @@ export interface RNCookieManager { get(url: string, useWebKit?: boolean): Promise clearAll(useWebKit?: boolean): Promise } -export const wrapReactNativeCookieManager = (rnc: RNCookieManager): CookieManager => ({ +export const wrapReactNativeCookieManager = ( + rnc: RNCookieManager +): CookieManager => ({ clearAll: () => rnc.clearAll().then(), getCookieString: async (url) => { const cookies = await rnc.get(url) return Object.values(cookies) - .map((c) => `${c.name}=${c.value}`).join('; ') + .map((c) => `${c.name}=${c.value}`) + .join('; ') }, getCookies: async (url) => { const cookies = await rnc.get(url) diff --git a/lib/fakeData.ts b/lib/fakeData.ts index 9beed58..edc105a 100644 --- a/lib/fakeData.ts +++ b/lib/fakeData.ts @@ -236,9 +236,11 @@ const data: any = { author: 'Vaktmästare Persson', header: 'Brandsläckare.', intro: 'Idag hade vi en incident med en brandsläckare.', - body: '## Information om brandsläckarincidenten\n\nHej, idag vid lunchtid utlöste en elev av misstag en pulverbrandsläckare i kapprummet. En del pulver yrde runt i rummet och under saneringen fick eleverna i angränsande klassrum vara i aulan istället för klassrummet.\n\nFlera elever var på plats i hallen när detta inträffade men utrymdes kort därefter. Pulvret är INTE hälsovådligt men kan ge upphov till halsirritation vid inandning.\n\nJag har pratat med berörda elever om det inträffade och uppmanat dem att ta hem kläder och tillhörigheter som fanns i kapprummet eftersom de troligen blivit dammiga. Vi rekommenderar att ni tvättar eller vädrar dessa.', + body: + '## Information om brandsläckarincidenten\n\nHej, idag vid lunchtid utlöste en elev av misstag en pulverbrandsläckare i kapprummet. En del pulver yrde runt i rummet och under saneringen fick eleverna i angränsande klassrum vara i aulan istället för klassrummet.\n\nFlera elever var på plats i hallen när detta inträffade men utrymdes kort därefter. Pulvret är INTE hälsovådligt men kan ge upphov till halsirritation vid inandning.\n\nJag har pratat med berörda elever om det inträffade och uppmanat dem att ta hem kläder och tillhörigheter som fanns i kapprummet eftersom de troligen blivit dammiga. Vi rekommenderar att ni tvättar eller vädrar dessa.', imageUrl: '6607f9b923edb6f85aa4417bab43c0f8.jpg', - fullImageUrl: 'https://cdn.breakit.se/assets/article/6607f9b923edb6f85aa4417bab43c0f8.jpg?d=980x500', + fullImageUrl: + 'https://cdn.breakit.se/assets/article/6607f9b923edb6f85aa4417bab43c0f8.jpg?d=980x500', imageAltText: 'Nyhetsbild. Bildtext ej tillgänglig.', published: '2020-08-16T21:10:00.000+02:0', modified: '2021-01-22T14:49:00.000+01:00', @@ -248,9 +250,11 @@ const data: any = { author: 'Ada L.', header: 'App, App, App', intro: 'Denna vecka bygger vi appar!', - body: '## Appar med öppen data \n\nDenna vecka har vi förmånen att få besök av några föräldrar som visar hur vi enkelt kan skapa appar som visar information ifrån öppna datakällor.\n\nEn fantastisk möjlighet att lära oss hur digitalisering skapar nya möjligheter i såväl skolan som arbetslivet.', + body: + '## Appar med öppen data \n\nDenna vecka har vi förmånen att få besök av några föräldrar som visar hur vi enkelt kan skapa appar som visar information ifrån öppna datakällor.\n\nEn fantastisk möjlighet att lära oss hur digitalisering skapar nya möjligheter i såväl skolan som arbetslivet.', imageUrl: '6607f9b923edb6f85aa4417bab43c0f8.jpg', - fullImageUrl: 'https://live.staticflickr.com/4063/4369776892_5cd42d27ba.jpg', + fullImageUrl: + 'https://live.staticflickr.com/4063/4369776892_5cd42d27ba.jpg', imageAltText: 'Nyhetsbild. Bildtext ej tillgänglig.', published: '2020-10-13T09:10:00.000+02:00', modified: '2021-02-09T15:45:00.000+02:00', @@ -260,9 +264,11 @@ const data: any = { author: 'Magister Svensson', header: 'Läxor vecka 6.', intro: 'Alla elever måste göra sina läxor!', - body: '## Läxor vecka 6 \n\nFöljande läxor är obligatoriska:\n\n- Antikens historia\n- Svenska stormaktstiden\n- Statistik A\n- Flerdimensionell analys, del 1', + body: + '## Läxor vecka 6 \n\nFöljande läxor är obligatoriska:\n\n- Antikens historia\n- Svenska stormaktstiden\n- Statistik A\n- Flerdimensionell analys, del 1', imageUrl: '6607f9b923edb6f85aa4417bab43c0f8.jpg', - fullImageUrl: 'https://www.mitti.se/_internal/cimg!0/ejf8efxee735ymm8tm40q3hhkl36sdt.jpeg', + fullImageUrl: + 'https://www.mitti.se/_internal/cimg!0/ejf8efxee735ymm8tm40q3hhkl36sdt.jpeg', imageAltText: 'Nyhetsbild. Bildtext ej tillgänglig.', published: '2020-08-16T21:10:00.000+02:0', modified: '2021-01-22T14:49:00.000+01:00', @@ -434,7 +440,8 @@ const data: any = { { title: 'Läsårsslut', id: 91, - description: '

 

', + description: + "

 

", location: null, startDate: '2021-06-11', endDate: '2021-06-11', @@ -443,7 +450,8 @@ const data: any = { { title: 'Fritids stängt', id: 92, - description: '

 

', + description: + "

 

", location: null, startDate: '2021-06-14', endDate: '2021-06-14', @@ -466,11 +474,13 @@ const data: any = { }, { title: 'Torsdag - Vecka 51', - description: 'Prinskorv potatis rödbetssallad +
Inlagd och senapssill', + description: + 'Prinskorv potatis rödbetssallad +
Inlagd och senapssill', }, { title: 'Fredag - Vecka 51', - description: 'Avslutning Varmkorv bröd ketchup senap
( F-3 i matsalen från 10:30 )', + description: + 'Avslutning Varmkorv bröd ketchup senap
( F-3 i matsalen från 10:30 )', }, ], notifications: [ @@ -478,8 +488,10 @@ const data: any = { id: '9025f9-a1e685-d7c4668f09-e14bc5-0ab', sender: 'Elevdokumentation', dateCreated: '2020-12-10T14:31:29.966Z', - message: 'Nu kan du ta del av ditt barns dokumentation av utvecklingssamtal', - url: 'https://www.breakit.se/artikel/21404/kodaren-slog-larm-nu-akutstoppas-skolplattformen-i-stockholm', + message: + 'Nu kan du ta del av ditt barns dokumentation av utvecklingssamtal', + url: + 'https://www.breakit.se/artikel/21404/kodaren-slog-larm-nu-akutstoppas-skolplattformen-i-stockholm', category: null, type: 'webnotify', }, @@ -488,7 +500,8 @@ const data: any = { sender: 'Planering och Bedömning', dateCreated: '2020-11-16T13:24:00.000Z', message: 'Ett nytt inlägg i en lärlogg har skapats.', - url: 'https://www.breakit.se/artikel/21423/har-ar-it-bolaget-bakom-haveriet-pa-skolplattformen', + url: + 'https://www.breakit.se/artikel/21423/har-ar-it-bolaget-bakom-haveriet-pa-skolplattformen', category: 'Lärlogg', type: 'avisering', }, @@ -497,7 +510,8 @@ const data: any = { sender: 'Planering och Bedömning', dateCreated: '2020-06-10T12:18:00.000Z', message: 'Nu finns det en bedömning att titta på.', - url: 'https://www.svt.se/nyheter/lokalt/stockholm/skolplattformen-i-stockholm-beratta-om-era-erfarenheter', + url: + 'https://www.svt.se/nyheter/lokalt/stockholm/skolplattformen-i-stockholm-beratta-om-era-erfarenheter', category: 'Bedömning', type: 'avisering', }, @@ -506,7 +520,8 @@ const data: any = { sender: 'Planering och Bedömning', dateCreated: '2020-03-24T14:28:00.000Z', message: 'Nu finns det en bedömning att titta på.', - url: 'https://www.breakit.se/artikel/18120/skolplattformen-kostade-700-miljoner-strid-med-entreprenor-om-varumarket', + url: + 'https://www.breakit.se/artikel/18120/skolplattformen-kostade-700-miljoner-strid-med-entreprenor-om-varumarket', category: 'Bedömning', type: 'avisering', }, @@ -515,7 +530,8 @@ const data: any = { sender: 'Planering och Bedömning', dateCreated: '2020-03-24T13:48:00.000Z', message: 'Nu finns det en bedömning att titta på.', - url: 'https://www.mitti.se/nyheter/forskolans-tur-att-fa-kritiserade-skolplattformen/lmsau!5338007/', + url: + 'https://www.mitti.se/nyheter/forskolans-tur-att-fa-kritiserade-skolplattformen/lmsau!5338007/', category: 'Bedömning', type: 'avisering', }, @@ -745,10 +761,13 @@ const data: any = { id: 'asdfasdfasdfa', author: 'Rektor Gustavsson', header: 'Välkommen till skolan!', - intro: 'Hej alla barn och föräldrar och välkomna till Storskolan! Här kommer en del information som kan vara bra att känna till inför första dagen.', - body: '## Information till föräldrar \n\nSkolan börjar kl 08.00 och slutar 18.00. Kommer man sent eller blir sjuk så ska det anmälas via Skolplattformen. Se till så att dina barn har ätit frukost. Frukt är nyttigt! \n\n## Information till barn\n\nLek är tillåtet på rasterna men enbart på skolgården. Medtag ej egna leksaker. Tvätta händerna.', + intro: + 'Hej alla barn och föräldrar och välkomna till Storskolan! Här kommer en del information som kan vara bra att känna till inför första dagen.', + body: + '## Information till föräldrar \n\nSkolan börjar kl 08.00 och slutar 18.00. Kommer man sent eller blir sjuk så ska det anmälas via Skolplattformen. Se till så att dina barn har ätit frukost. Frukt är nyttigt! \n\n## Information till barn\n\nLek är tillåtet på rasterna men enbart på skolgården. Medtag ej egna leksaker. Tvätta händerna.', imageUrl: '6607f9b923edb6f85aa4417bab43c0f8.jpg', - fullImageUrl: 'https://timbro.se/app/uploads/2020/10/broman-skolplattformen-1280x752.jpg', + fullImageUrl: + 'https://timbro.se/app/uploads/2020/10/broman-skolplattformen-1280x752.jpg', imageAltText: 'Nyhetsbild. Bildtext ej tillgänglig.', published: '2020-08-16T21:10:00.000+02:0', modified: '2021-01-22T14:49:00.000+01:00', @@ -758,9 +777,11 @@ const data: any = { author: 'Ada L.', header: 'App, App, App', intro: 'Denna vecka bygger vi appar!', - body: '## Appar med öppen data \n\nDenna vecka har vi förmånen att få besök av några föräldrar som visar hur vi enkelt kan skapa appar som visar information ifrån öppna datakällor.\n\nEn fantastisk möjlighet att lära oss hur digitalisering skapar nya möjligheter i såväl skolan som arbetslivet.', + body: + '## Appar med öppen data \n\nDenna vecka har vi förmånen att få besök av några föräldrar som visar hur vi enkelt kan skapa appar som visar information ifrån öppna datakällor.\n\nEn fantastisk möjlighet att lära oss hur digitalisering skapar nya möjligheter i såväl skolan som arbetslivet.', imageUrl: '6607f9b923edb6f85aa4417bab43c0f8.jpg', - fullImageUrl: 'https://live.staticflickr.com/4063/4369776892_5cd42d27ba.jpg', + fullImageUrl: + 'https://live.staticflickr.com/4063/4369776892_5cd42d27ba.jpg', imageAltText: 'Nyhetsbild. Bildtext ej tillgänglig.', published: '2020-10-13T09:10:00.000+02:00', modified: '2021-02-09T15:45:00.000+02:00', @@ -770,9 +791,11 @@ const data: any = { author: 'Magister Svensson', header: 'Läxor vecka 6.', intro: 'Alla elever måste göra sina läxor!', - body: '## Läxor vecka 6 \n\nFöljande läxor är obligatoriska:\n\n- Antikens historia\n- Svenska stormaktstiden\n- Statistik A\n- Flerdimensionell analys, del 1', + body: + '## Läxor vecka 6 \n\nFöljande läxor är obligatoriska:\n\n- Antikens historia\n- Svenska stormaktstiden\n- Statistik A\n- Flerdimensionell analys, del 1', imageUrl: '6607f9b923edb6f85aa4417bab43c0f8.jpg', - fullImageUrl: 'https://www.mitti.se/_internal/cimg!0/ejf8efxee735ymm8tm40q3hhkl36sdt.jpeg', + fullImageUrl: + 'https://www.mitti.se/_internal/cimg!0/ejf8efxee735ymm8tm40q3hhkl36sdt.jpeg', imageAltText: 'Nyhetsbild. Bildtext ej tillgänglig.', published: '2020-08-16T21:10:00.000+02:0', modified: '2021-01-22T14:49:00.000+01:00', @@ -781,8 +804,10 @@ const data: any = { id: 'asdfasdfasdfd', author: 'Information från Förskoleklass', header: 'Vinteraktiviteter', - intro: 'Vi kommer efter att förskoleklassen är slut arrangera olika vinteraktiviteter genom fridtidsverksamheten.', - body: '## Vänligen ta med hjälm, skridskor eller stjärtlapp. Alla barn måste ha hjälm på sig samt varma kläder. Vi kommer åka i backen bakom skolbyggnaden samt använda isen som spolats vid Mullsjöskolan. Personal kommer finnas på plats samt att vi erbjuda varm dryck, frukt och lek för de barn som ej har hjälm eller lämpligt åkdon.', + intro: + 'Vi kommer efter att förskoleklassen är slut arrangera olika vinteraktiviteter genom fridtidsverksamheten.', + body: + '## Vänligen ta med hjälm, skridskor eller stjärtlapp. Alla barn måste ha hjälm på sig samt varma kläder. Vi kommer åka i backen bakom skolbyggnaden samt använda isen som spolats vid Mullsjöskolan. Personal kommer finnas på plats samt att vi erbjuda varm dryck, frukt och lek för de barn som ej har hjälm eller lämpligt åkdon.', imageUrl: '6607f9b923edb6f85aa4417bab43c0f8.jpg', fullImageUrl: 'https://unsplash.com/photos/yB_aiAWkm40', imageAltText: 'Nyhetsbild. Bildtext ej tillgänglig.', @@ -794,7 +819,8 @@ const data: any = { author: 'Köket', header: 'Ekologisk vecka i matsalen', intro: 'Ekologiska veckan i matsalen vecka 11', - body: '## Vi kommer ha tema jorden i matsalen och servera ekologisk mat från hela världen med tema jorden. Detta för att belysa att man kan använda alla delar av råvaorna. Det kommer erbjudas rätter från alla världsdelar som är producerat för jordens bästa. Smaklig spis hälsar Gunnel i köket med personal.', + body: + '## Vi kommer ha tema jorden i matsalen och servera ekologisk mat från hela världen med tema jorden. Detta för att belysa att man kan använda alla delar av råvaorna. Det kommer erbjudas rätter från alla världsdelar som är producerat för jordens bästa. Smaklig spis hälsar Gunnel i köket med personal.', imageUrl: '6607f9b923edb6f85aa4417bab43c0f8.jpg', fullImageUrl: 'https://unsplash.com/photos/7K17MvT8qBg', imageAltText: 'Nyhetsbild. Bildtext ej tillgänglig.', @@ -805,8 +831,10 @@ const data: any = { id: 'asdfasdfasdfbvdsa', author: 'Vaktmästaren', header: 'Klotter i korridoren (igen)', - intro: 'Ännu en gång har vi råka ut för skadegörelse i korridorerna vid åk 5', - body: '## Tyvärr har flera elever klottat på skåp och väggar vid åk5 skåpen. Detta är helt oacceptablet beteende och kostar skolan stora belopp att åtgärda. Vi ber alla föräldrar prata med sina barn om klotter samt att det var väldigt grovt spårkbruk. Personalen på skolan kommer att hålla extra uppsikt och vi har även pratat med en del av de inblandade eleverna i denna skadegörelse.\n\nPersonalen har även börjat forska på vad vissa av de skrivna orden betyder och Eva-Britt är förfasad över språkbruket samt vad de innebär. Bernt kommer att påbörja saneringen och återställningen av skadegörelsen samt vakta korridorerna nogrannare för att säkerställa att detta ej kommer ske igen. \nUPPDATERING: Det som är skrivet om Sara är inte sant! ', + intro: + 'Ännu en gång har vi råka ut för skadegörelse i korridorerna vid åk 5', + body: + '## Tyvärr har flera elever klottat på skåp och väggar vid åk5 skåpen. Detta är helt oacceptablet beteende och kostar skolan stora belopp att åtgärda. Vi ber alla föräldrar prata med sina barn om klotter samt att det var väldigt grovt spårkbruk. Personalen på skolan kommer att hålla extra uppsikt och vi har även pratat med en del av de inblandade eleverna i denna skadegörelse.\n\nPersonalen har även börjat forska på vad vissa av de skrivna orden betyder och Eva-Britt är förfasad över språkbruket samt vad de innebär. Bernt kommer att påbörja saneringen och återställningen av skadegörelsen samt vakta korridorerna nogrannare för att säkerställa att detta ej kommer ske igen. \nUPPDATERING: Det som är skrivet om Sara är inte sant! ', imageUrl: '6607f9b923edb6f85aa4417bab43c0f8.jpg', fullImageUrl: 'https://unsplash.com/photos/SkbEZ16VywM', imageAltText: 'Nyhetsbild. Bildtext ej tillgänglig.', @@ -994,11 +1022,13 @@ const data: any = { }, { title: 'Torsdag - Vecka 51', - description: 'Prinskorv potatis rödbetssallad +
Inlagd och senapssill', + description: + 'Prinskorv potatis rödbetssallad +
Inlagd och senapssill', }, { title: 'Fredag - Vecka 51', - description: 'Avslutning Varmkorv bröd ketchup senap
( F-3 i matsalen från 10:30 )', + description: + 'Avslutning Varmkorv bröd ketchup senap
( F-3 i matsalen från 10:30 )', }, ], notifications: [ @@ -1007,7 +1037,8 @@ const data: any = { sender: 'Planering och Bedömning', dateCreated: '2020-12-02T14:02:00.000Z', message: 'Ett nytt inlägg i en lärlogg har skapats.', - url: 'https://www.mitti.se/nyheter/rekorddyr-skolplattform-kostar-258-miljoner-till/lmsao!5381301/', + url: + 'https://www.mitti.se/nyheter/rekorddyr-skolplattform-kostar-258-miljoner-till/lmsao!5381301/', category: 'Lärlogg', messageType: 'avisering', }, @@ -1016,7 +1047,8 @@ const data: any = { sender: 'Planering och Bedömning', dateCreated: '2020-12-01T12:43:00.000Z', message: 'Ett nytt inlägg i en lärlogg har skapats.', - url: 'https://computersweden.idg.se/2.2683/1.722561/lacka-skolplattformen-datainspektionen', + url: + 'https://computersweden.idg.se/2.2683/1.722561/lacka-skolplattformen-datainspektionen', category: 'Lärlogg', messageType: 'avisering', }, @@ -1034,7 +1066,8 @@ const data: any = { sender: 'Planering och Bedömning', dateCreated: '2020-11-16T13:24:00.000Z', message: 'Ett nytt inlägg i en lärlogg har skapats.', - url: 'https://www.breakit.se/artikel/27075/skolplattformen-kostade-1-miljard-att-bygga-nu-tvingas-stockholm-bota', + url: + 'https://www.breakit.se/artikel/27075/skolplattformen-kostade-1-miljard-att-bygga-nu-tvingas-stockholm-bota', category: 'Lärlogg', messageType: 'avisering', }, @@ -1043,7 +1076,8 @@ const data: any = { sender: 'Planering och Bedömning', dateCreated: '2020-11-12T13:27:00.000Z', message: 'Ett nytt inlägg i en lärlogg har skapats.', - url: 'https://www.nyteknik.se/sakerhet/ygeman-om-datalackan-i-skolplattformen-det-ar-upprorande-6968853', + url: + 'https://www.nyteknik.se/sakerhet/ygeman-om-datalackan-i-skolplattformen-det-ar-upprorande-6968853', category: 'Lärlogg', messageType: 'avisering', }, @@ -1051,14 +1085,12 @@ const data: any = { }, } -export const user = (): User => ( - { - firstName: 'Namn', - lastName: 'Namnsson', - } -) +export const user = (): User => ({ + firstName: 'Namn', + lastName: 'Namnsson', +}) -export const children = (): Child[] => ([ +export const children = (): Child[] => [ { name: 'Shanel Nilsson (elev)', id: '39b59e-bf4b9f-f68ac25321-977218-bf0', @@ -1073,28 +1105,20 @@ export const children = (): Child[] => ([ status: 'GR', schoolId: '8e6b13b-3116-e66c39b-a4c3fa5-a1d72d9', }, -]) +] -export const classmates = (child: Child): Classmate[] => ( +export const classmates = (child: Child): Classmate[] => data[child.id].classmates -) -export const news = (child: Child): NewsItem[] => ( - data[child.id].news -) +export const news = (child: Child): NewsItem[] => data[child.id].news -export const calendar = (child: Child): CalendarItem[] => ( +export const calendar = (child: Child): CalendarItem[] => data[child.id].calendar -) -export const schedule = (child: Child): ScheduleItem[] => ( +export const schedule = (child: Child): ScheduleItem[] => data[child.id].schedule -) -export const menu = (child: Child): MenuItem[] => ( - data[child.id].menu -) +export const menu = (child: Child): MenuItem[] => data[child.id].menu -export const notifications = (child: Child): Notification[] => ( +export const notifications = (child: Child): Notification[] => data[child.id].notifications -) diff --git a/lib/fetcher.ts b/lib/fetcher.ts index cbe9eac..cc46b35 100644 --- a/lib/fetcher.ts +++ b/lib/fetcher.ts @@ -10,7 +10,10 @@ export interface CallInfo extends RequestInit { } export interface FetcherOptions { - record?: (info: CallInfo, data: string | Blob | ArrayBuffer | any) => Promise + record?: ( + info: CallInfo, + data: string | Blob | ArrayBuffer | any + ) => Promise } export interface Fetcher { @@ -28,7 +31,7 @@ const record = async ( type: string, options: FetcherOptions, response: Response, - data: string | ArrayBuffer | Blob | any, + data: string | ArrayBuffer | Blob | any ): Promise => { if (!options.record) { return @@ -44,13 +47,21 @@ const record = async ( await options.record(info, data) } -export default function wrap(fetch: Fetch, options: FetcherOptions = {}): Fetcher { - return async (name: string, url: string, init: RequestInit = { headers: {} }): Promise => { +export default function wrap( + fetch: Fetch, + options: FetcherOptions = {} +): Fetcher { + return async ( + name: string, + url: string, + init: RequestInit = { headers: {} } + ): Promise => { const config = { ...init, headers: { - // eslint-disable-next-line max-len - 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', + 'User-Agent': + // eslint-disable-next-line max-len + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', ...init.headers, }, } diff --git a/lib/index.ts b/lib/index.ts index f25d063..ac9b7a0 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -14,9 +14,10 @@ export { LoginStatusChecker } from './loginStatus' const init = ( fetch: Fetch, - cookieManagerImpl: RNCookieManager|ToughCookieJar, - options?: FetcherOptions, + cookieManagerImpl: RNCookieManager | ToughCookieJar, + options?: FetcherOptions ): Api => { + // prettier-ignore const cookieManager = ((cookieManagerImpl as RNCookieManager).get) ? wrapReactNativeCookieManager(cookieManagerImpl as RNCookieManager) : wrapToughCookie(cookieManagerImpl as ToughCookieJar) diff --git a/lib/loginStatus.ts b/lib/loginStatus.ts index 7de8d5b..5dc7e24 100644 --- a/lib/loginStatus.ts +++ b/lib/loginStatus.ts @@ -14,8 +14,10 @@ export enum LoginEvent { export interface LoginStatusChecker { token: string - on: (event: 'OK' | 'PENDING' | 'ERROR' | 'USER_SIGN' | 'CANCELLED', - listener: (...args: any[]) => void) => LoginStatusChecker + on: ( + event: 'OK' | 'PENDING' | 'ERROR' | 'USER_SIGN' | 'CANCELLED', + listener: (...args: any[]) => void + ) => LoginStatusChecker cancel: () => Promise } @@ -40,7 +42,12 @@ class Checker extends EventEmitter { const response = await this.fetcher('login-status', this.url) const status = await response.text() this.emit(status) - if (!this.cancelled && status !== 'OK' && status !== 'ERROR!' && status !== 'CANCELLED') { + if ( + !this.cancelled && + status !== 'OK' && + status !== 'ERROR!' && + status !== 'CANCELLED' + ) { setTimeout(() => this.check(), 1000) } } @@ -50,6 +57,7 @@ class Checker extends EventEmitter { } } -export const checkStatus = (fetch: Fetcher, ticket: AuthTicket): LoginStatusChecker => ( - new Checker(fetch, ticket) -) +export const checkStatus = ( + fetch: Fetcher, + ticket: AuthTicket +): LoginStatusChecker => new Checker(fetch, ticket) diff --git a/lib/parse.test.ts b/lib/parse.test.ts index cd2c799..5878419 100644 --- a/lib/parse.test.ts +++ b/lib/parse.test.ts @@ -1,4 +1,4 @@ -import * as parse from "./parse" +import * as parse from './parse' describe('parse', () => { let response: parse.EtjanstResponse @@ -10,8 +10,8 @@ describe('parse', () => { Data: [ { Name: 'Some name', - } - ] + }, + ], } }) it('returns data on success', () => { @@ -42,24 +42,27 @@ describe('parse', () => { SchoolName: null, GroupId: null, GroupName: null, - Classes: 'VHsidan_0495CABC-77DB-41D7-824B-8B4D63E50D15;Section_AD1BB3B2-C1EE-4DFE-8209-CB6D42CE23D7;Section_0E67D0BF-594C-4C1B-9291-E753926DCD40;VHsidan_1C94EC54-9798-401C-B973-2454246D95DA', + Classes: + 'VHsidan_0495CABC-77DB-41D7-824B-8B4D63E50D15;Section_AD1BB3B2-C1EE-4DFE-8209-CB6D42CE23D7;Section_0E67D0BF-594C-4C1B-9291-E753926DCD40;VHsidan_1C94EC54-9798-401C-B973-2454246D95DA', isSameSDSId: false, ResultUnitId: null, ResultUnitName: null, UnitId: null, - UnitName: null - } - ] + UnitName: null, + }, + ], } }) it('parses children correctly', () => { - expect(parse.children(response)).toEqual([{ - name: 'Some name', - id: '42C3997E-D772-423F-9290-6FEEB3CB2DA7', - sdsId: '786E3393-F044-4660-9105-B444DEB289AA', - schoolId: 'DE2E1293-0F40-4B91-9D91-1E99355DC257', - status: 'GR', - }]) + expect(parse.children(response)).toEqual([ + { + name: 'Some name', + id: '42C3997E-D772-423F-9290-6FEEB3CB2DA7', + sdsId: '786E3393-F044-4660-9105-B444DEB289AA', + schoolId: 'DE2E1293-0F40-4B91-9D91-1E99355DC257', + status: 'GR', + }, + ]) }) }) describe('calendar', () => { @@ -88,19 +91,21 @@ describe('parse', () => { ListId: null, Mentor: null, }, - ] + ], } }) it('parses calendar correctly', () => { - expect(parse.calendar(response)).toEqual([{ - id: 29, - location: null, - title: 'Jullov', - description: 'hello', - startDate: '2020-12-21T09:00:00.000+01:00', - endDate: '2021-01-08T10:00:00.000+01:00', - allDay: false, - }]) + expect(parse.calendar(response)).toEqual([ + { + id: 29, + location: null, + title: 'Jullov', + description: 'hello', + startDate: '2020-12-21T09:00:00.000+01:00', + endDate: '2021-01-08T10:00:00.000+01:00', + allDay: false, + }, + ]) }) }) describe('classmates', () => { @@ -142,72 +147,76 @@ describe('parse', () => { GROUPTYPE: null, STUDENT_FIRSTNAME: null, STUDENT_LASTNAME: null, - STUDENT_ID: null - } + STUDENT_ID: null, + }, ], ClassName: '7C', - ClassId: 'B2BF465B-581B-43AC-9CA7-F11BB0ED4646' + ClassId: 'B2BF465B-581B-43AC-9CA7-F11BB0ED4646', }, - ] + ], } }) it('parses class mates correctly', () => { - expect(parse.classmates(response)).toEqual([{ - sisId: '22F0CFC7-09C7-45DC-9388-AE9A9EA1356B', - firstname: 'Bo', - lastname: 'Burström', - className: '7C', - guardians: [ - { - firstname: 'Allan', - lastname: 'Fridell', - address: 'Hult södregård', - mobile: '0690-6346216', - email: 'allan.fridell@mailinater.com', - } - ] - }]) + expect(parse.classmates(response)).toEqual([ + { + sisId: '22F0CFC7-09C7-45DC-9388-AE9A9EA1356B', + firstname: 'Bo', + lastname: 'Burström', + className: '7C', + guardians: [ + { + firstname: 'Allan', + lastname: 'Fridell', + address: 'Hult södregård', + mobile: '0690-6346216', + email: 'allan.fridell@mailinater.com', + }, + ], + }, + ]) }) }) describe('schedule', () => { beforeEach(() => { response = { - "Success": true, - "Error": null, - "Data": [ + Success: true, + Error: null, + Data: [ { - "Title": "Canceled: Julavslutning 8C", - "Id": 0, - "Description": "Nåt kul", - "Location": "Lakritskolan", - "EventDate": "2020-12-14", - "EventDateTime": "14:10", - "LongEventDateTime": "2020-12-14 14:10", - "EndDate": "2020-12-14", - "EndDateTime": "14:40", - "LongEndDateTime": "2020-12-14 14:40", - "EventDateDayNumber": "14", - "EventDateMonthName": "dec", - "EventDateMonthFullName": "december", - "FullDateDescription": "2020-12-14 14:10 - 2020-12-14 14:40", - "IsSameDay": true, - "AllDayEvent": false, - "ListId": null, - "Mentor": null - } - ] + Title: 'Canceled: Julavslutning 8C', + Id: 0, + Description: 'Nåt kul', + Location: 'Lakritskolan', + EventDate: '2020-12-14', + EventDateTime: '14:10', + LongEventDateTime: '2020-12-14 14:10', + EndDate: '2020-12-14', + EndDateTime: '14:40', + LongEndDateTime: '2020-12-14 14:40', + EventDateDayNumber: '14', + EventDateMonthName: 'dec', + EventDateMonthFullName: 'december', + FullDateDescription: '2020-12-14 14:10 - 2020-12-14 14:40', + IsSameDay: true, + AllDayEvent: false, + ListId: null, + Mentor: null, + }, + ], } }) it('parses schedule correctly', () => { - expect(parse.schedule(response)).toEqual([{ - title: 'Canceled: Julavslutning 8C', - description: 'Nåt kul', - location: 'Lakritskolan', - startDate: '2020-12-14T14:10:00.000+01:00', - endDate: '2020-12-14T14:40:00.000+01:00', - oneDayEvent: true, - allDayEvent: false, - }]) + expect(parse.schedule(response)).toEqual([ + { + title: 'Canceled: Julavslutning 8C', + description: 'Nåt kul', + location: 'Lakritskolan', + startDate: '2020-12-14T14:10:00.000+01:00', + endDate: '2020-12-14T14:40:00.000+01:00', + oneDayEvent: true, + allDayEvent: false, + }, + ]) }) }) describe('news', () => { @@ -220,23 +229,28 @@ describe('parse', () => { NewsItems: [ { NewsId: 'news id', - SiteId: 'elevstockholm.sharepoint.com,27892ACC-BA2E-4DEC-97B8-25F7098C3BF6,A239466A-9A52-42FF-8A3F-D94C342F2700', + SiteId: + 'elevstockholm.sharepoint.com,27892ACC-BA2E-4DEC-97B8-25F7098C3BF6,A239466A-9A52-42FF-8A3F-D94C342F2700', NewsListId: '3EC323A1-EA16-4D24-84C8-DAA49E76F9F4', - NewsItemId: 'elevstockholm.sharepoint.com,27892ACC-BA2E-4DEC-97B8-25F7098C3BF6,A239466A-9A52-42FF-8A3F-D94C342F2700_99', - Header: 'Problemet med att se betyg i bild, slöjd och teknik löst!', + NewsItemId: + 'elevstockholm.sharepoint.com,27892ACC-BA2E-4DEC-97B8-25F7098C3BF6,A239466A-9A52-42FF-8A3F-D94C342F2700_99', + Header: + 'Problemet med att se betyg i bild, slöjd och teknik löst!', PublicationDate: '/Date(1608304542000)/', PubDateSE: '18 december 2020 16:15', ModifiedDate: '/Date(1608304680000)/', ModDateSE: '18 december 2020 16:18', Source: 'Livets hårda skolklasser', - Preamble: 'Hej,Nu är problemet löst! Alla betyg syns som de ska.God jul!...', + Preamble: + 'Hej,Nu är problemet löst! Alla betyg syns som de ska.God jul!...', BannerImageUrl: 'A703552D-DBF3-45B0-8E67-6E062105A0C5.jpeg', BannerImageGuid: 'A703552D-DBF3-45B0-8E67-6E062105A0C5', BannerImageListId: 'FFBE49E9-BDE1-4C75-BA0E-D98D4E2FCF21', - Body: '

Hej,

Nu är problemet löst! Alla betyg syns som de ska. 

God jul!

', + Body: + '

Hej,

Nu är problemet löst! Alla betyg syns som de ska. 

God jul!

', BodyNoHtml: null, AuthorDisplayName: 'Eva-Lotta Rönnberg', - altText: 'Nyhetsbild. Bildtext ej tillgänglig.' + altText: 'Nyhetsbild. Bildtext ej tillgänglig.', }, ], ViewGlobalTranslations: {}, @@ -258,10 +272,10 @@ describe('parse', () => { 'Zip', 'Address', 'ValidationRequiredFieldMessage', - 'ValidationErrorMessage' + 'ValidationErrorMessage', ], - LocalTranslationIds: ['IndexPageHeading1'] - } + LocalTranslationIds: ['IndexPageHeading1'], + }, } }) it('parses news items (except body) correctly', () => { @@ -269,77 +283,91 @@ describe('parse', () => { expect(item.id).toEqual('news id') expect(item.author).toEqual('Eva-Lotta Rönnberg') - expect(item.header).toEqual('Problemet med att se betyg i bild, slöjd och teknik löst!') - expect(item.imageUrl).toEqual('A703552D-DBF3-45B0-8E67-6E062105A0C5.jpeg') - expect(item.fullImageUrl).toEqual('https://etjanst.stockholm.se/Vardnadshavare/inloggad2/NewsBanner?url=A703552D-DBF3-45B0-8E67-6E062105A0C5.jpeg') - expect(item.imageAltText).toEqual('Nyhetsbild. Bildtext ej tillgänglig.') - expect(item.intro).toEqual('Hej,Nu är problemet löst! Alla betyg syns som de ska.God jul!...') + expect(item.header).toEqual( + 'Problemet med att se betyg i bild, slöjd och teknik löst!' + ) + expect(item.imageUrl).toEqual( + 'A703552D-DBF3-45B0-8E67-6E062105A0C5.jpeg' + ) + expect(item.fullImageUrl).toEqual( + 'https://etjanst.stockholm.se/Vardnadshavare/inloggad2/NewsBanner?url=A703552D-DBF3-45B0-8E67-6E062105A0C5.jpeg' + ) + expect(item.imageAltText).toEqual( + 'Nyhetsbild. Bildtext ej tillgänglig.' + ) + expect(item.intro).toEqual( + 'Hej,Nu är problemet löst! Alla betyg syns som de ska.God jul!...' + ) expect(item.modified).toEqual('2020-12-18T16:18:00.000+01:00') expect(item.published).toEqual('2020-12-18T16:15:00.000+01:00') }) it('parses body correctly', () => { const [item] = parse.news(response) - const expected = 'Hej, Nu är problemet löst! Alla betyg syns som de ska. God jul!' - const trimmed = (item.body || '').split('\n').map(t => t.trim()).join(' ') + const expected = + 'Hej, Nu är problemet löst! Alla betyg syns som de ska. God jul!' + const trimmed = (item.body || '') + .split('\n') + .map((t) => t.trim()) + .join(' ') expect(trimmed).toEqual(expected) }) }) - describe('newsItem', () => { beforeEach(() => { response = { - "Success": true, - "Error": null, - "Data": { - "CurrentNewsItem": { - "NewsId": '123', - "SiteId": "elevstockholm.sharepoint.com,d112c398-71d4-468f-9a59-84d806751b08,3addab10-546a-4551-8076-72c9cd67f961", - "NewsListId": "95df7d70-fbf0-470d-9926-e4e633f77f27", - "NewsItemId": "elevstockholm.sharepoint.com,d112c398-71d4-468f-9a59-84d806751b08,3addab10-546a-4551-8076-72c9cd67f961_40", - "Header": "Avlusningsdagarna 5-7 februari 2021", - "PublicationDate": "/Date(1612445471000)/", - "PubDateSE": "4 februari 2021 14:31", - "ModifiedDate": "/Date(1612445852000)/", - "ModDateSE": "14 februari 2021 14:37", - "Source": "Södra Ängby skola", - "Preamble": "Kära vårdnadshavare!I helgen är det avlusningsdagar!", - "BannerImageUrl": "123123.jpeg", - "BannerImageGuid": "7a8142d9d9d54cf090e8457e4c629227", - "BannerImageListId": "a88c22e8-7094-4a71-b4fd-8792c62a7b4a", - "Body": "

Kära vårdnadshavare!

I helgen är det avlusningsdagar! Ta tillfället i akt att luskamma ditt barn

Du finner all info du behöver på 1177 hemsida ​​​​​​​

Trevlig helg!

​​​​​​​

", - "BodyNoHtml": null, - "AuthorDisplayName": "Tieto Evry", - "altText": null, - "OriginalSourceUrl": null + Success: true, + Error: null, + Data: { + CurrentNewsItem: { + NewsId: '123', + SiteId: + 'elevstockholm.sharepoint.com,d112c398-71d4-468f-9a59-84d806751b08,3addab10-546a-4551-8076-72c9cd67f961', + NewsListId: '95df7d70-fbf0-470d-9926-e4e633f77f27', + NewsItemId: + 'elevstockholm.sharepoint.com,d112c398-71d4-468f-9a59-84d806751b08,3addab10-546a-4551-8076-72c9cd67f961_40', + Header: 'Avlusningsdagarna 5-7 februari 2021', + PublicationDate: '/Date(1612445471000)/', + PubDateSE: '4 februari 2021 14:31', + ModifiedDate: '/Date(1612445852000)/', + ModDateSE: '14 februari 2021 14:37', + Source: 'Södra Ängby skola', + Preamble: 'Kära vårdnadshavare!I helgen är det avlusningsdagar!', + BannerImageUrl: '123123.jpeg', + BannerImageGuid: '7a8142d9d9d54cf090e8457e4c629227', + BannerImageListId: 'a88c22e8-7094-4a71-b4fd-8792c62a7b4a', + Body: + '

Kära vårdnadshavare!

I helgen är det avlusningsdagar! Ta tillfället i akt att luskamma ditt barn

Du finner all info du behöver på 1177 hemsida ​​​​​​​

Trevlig helg!

​​​​​​​

', + BodyNoHtml: null, + AuthorDisplayName: 'Tieto Evry', + altText: null, + OriginalSourceUrl: null, }, - "CurrentChild": null, - "ViewGlobalTranslations": {}, - "ViewLocalTranslations": {}, - "Children": null, - "Status": null, - "GlobalTranslationIds": [ - "InformationalHeader", - "ContactUsMessageLabel", - "Send", - "RequiredFieldMessageInfo", - "Sex", - "Male", - "Female", - "SSN", - "FirstName", - "LastName", - "Email", - "Zip", - "Address", - "ValidationRequiredFieldMessage", - "ValidationErrorMessage" + CurrentChild: null, + ViewGlobalTranslations: {}, + ViewLocalTranslations: {}, + Children: null, + Status: null, + GlobalTranslationIds: [ + 'InformationalHeader', + 'ContactUsMessageLabel', + 'Send', + 'RequiredFieldMessageInfo', + 'Sex', + 'Male', + 'Female', + 'SSN', + 'FirstName', + 'LastName', + 'Email', + 'Zip', + 'Address', + 'ValidationRequiredFieldMessage', + 'ValidationErrorMessage', ], - "LocalTranslationIds": [ - "IndexPageHeading1" - ] - } + LocalTranslationIds: ['IndexPageHeading1'], + }, } }) it('parses news details (except body) correctly', () => { @@ -348,7 +376,9 @@ describe('parse', () => { expect(item.id).toEqual('123') expect(item.header).toEqual('Avlusningsdagarna 5-7 februari 2021') expect(item.imageUrl).toEqual('123123.jpeg') - expect(item.intro).toEqual('Kära vårdnadshavare!I helgen är det avlusningsdagar!') + expect(item.intro).toEqual( + 'Kära vårdnadshavare!I helgen är det avlusningsdagar!' + ) expect(item.published).toEqual('2021-02-04T14:31:00.000+01:00') expect(item.modified).toEqual('2021-02-14T14:37:00.000+01:00') expect(item.author).toEqual('Tieto Evry') @@ -356,12 +386,12 @@ describe('parse', () => { it('parses body correctly', () => { const item = parse.newsItemDetails(response) - const expected = '[1177 hemsida](https://www.1177.se/sjukdomar--besvar/hud-har-och-naglar/harbotten-och-harsackar/huvudloss/)​​​​​​​' + const expected = + '[1177 hemsida](https://www.1177.se/sjukdomar--besvar/hud-har-och-naglar/harbotten-och-harsackar/huvudloss/)​​​​​​​' expect(item.body).toContain(expected) }) }) - describe('menu', () => { beforeEach(() => { response = { @@ -370,16 +400,18 @@ describe('parse', () => { Data: [ { Title: 'Måndag - Vecka 52', - Description: 'Körrfärsrätt .
Veg färs' + Description: 'Körrfärsrätt .
Veg färs', }, ], } }) it('parses menu correctly', () => { - expect(parse.menu(response)).toEqual([{ - title: 'Måndag - Vecka 52', - description: 'Körrfärsrätt .\nVeg färs' - }]) + expect(parse.menu(response)).toEqual([ + { + title: 'Måndag - Vecka 52', + description: 'Körrfärsrätt .\nVeg färs', + }, + ]) }) }) @@ -389,34 +421,34 @@ describe('parse', () => { Success: true, Error: null, Data: { - "SelectedWeek": 12, - "Menus": [ - { - "Week": "12", - "Mon": "Köttfärslimpa med sås och potatis", - "Tue": "Curryfisk med ris", - "Wed": "Tagliatelle med vegetarisk sås", - "Thu": "Chorizo med stuvad potatis", - "Fri": "Ört och vitlöksinbakad fisk, potatis" - }, - { - "Week": "19", - "Mon": "FISKGRATÄNG WALEWSKA", - "Tue": "STEKT FLÄSK MED RAGGMUNK", - "Wed": "PENNEPASTA MED TONFISK", - "Thu": "KÖTTGRYTA MED POTATIS", - "Fri": "GRÖNSAKSGRATÄNG MED TZATZIKI" - }, - { - "Week": "20", - "Mon": "SPAGHETTI SALMONE ", - "Tue": "STEKT FALUKORV MED SENAPSSÅS OCH POTATIS", - "Wed": "SOPPA MED RISONI OCH HEMBAKAT BRÖD", - "Thu": "PANERAD FISK MED SKAGEN OCH POTATIS", - "Fri": "TACOS" - } - ] - } + SelectedWeek: 12, + Menus: [ + { + Week: '12', + Mon: 'Köttfärslimpa med sås och potatis', + Tue: 'Curryfisk med ris', + Wed: 'Tagliatelle med vegetarisk sås', + Thu: 'Chorizo med stuvad potatis', + Fri: 'Ört och vitlöksinbakad fisk, potatis', + }, + { + Week: '19', + Mon: 'FISKGRATÄNG WALEWSKA', + Tue: 'STEKT FLÄSK MED RAGGMUNK', + Wed: 'PENNEPASTA MED TONFISK', + Thu: 'KÖTTGRYTA MED POTATIS', + Fri: 'GRÖNSAKSGRATÄNG MED TZATZIKI', + }, + { + Week: '20', + Mon: 'SPAGHETTI SALMONE ', + Tue: 'STEKT FALUKORV MED SENAPSSÅS OCH POTATIS', + Wed: 'SOPPA MED RISONI OCH HEMBAKAT BRÖD', + Thu: 'PANERAD FISK MED SKAGEN OCH POTATIS', + Fri: 'TACOS', + }, + ], + }, } }) it('parses menu correctly', () => { @@ -425,24 +457,24 @@ describe('parse', () => { expect(result).toEqual([ { title: 'Måndag - Vecka 12', - description: 'Köttfärslimpa med sås och potatis' + description: 'Köttfärslimpa med sås och potatis', }, { title: 'Tisdag - Vecka 12', - description: 'Curryfisk med ris' + description: 'Curryfisk med ris', }, { title: 'Onsdag - Vecka 12', - description: 'Tagliatelle med vegetarisk sås' + description: 'Tagliatelle med vegetarisk sås', }, { title: 'Torsdag - Vecka 12', - description: 'Chorizo med stuvad potatis' + description: 'Chorizo med stuvad potatis', }, { title: 'Fredag - Vecka 12', - description: 'Ört och vitlöksinbakad fisk, potatis' - } + description: 'Ört och vitlöksinbakad fisk, potatis', + }, ]) }) }) @@ -456,7 +488,8 @@ describe('parse', () => { userFirstName: 'Per-Ola', userLastName: 'Assarsson', userEmail: 'per-ola.assarsson@dodgit.com', - notificationId: 'B026594053D44299AB64ED81990B49C04D32F635C9A3454A84030439BFDDEF04' + notificationId: + 'B026594053D44299AB64ED81990B49C04D32F635C9A3454A84030439BFDDEF04', } }) it('parses user correctly', () => { @@ -466,7 +499,8 @@ describe('parse', () => { lastName: 'Assarsson', email: 'per-ola.assarsson@dodgit.com', isAuthenticated: true, - notificationId: 'B026594053D44299AB64ED81990B49C04D32F635C9A3454A84030439BFDDEF04', + notificationId: + 'B026594053D44299AB64ED81990B49C04D32F635C9A3454A84030439BFDDEF04', }) }) }) @@ -480,49 +514,54 @@ describe('parse', () => { Notification: { Messageid: 'E2E3A567-307F-4859-91BA-31B1F4522A7B', Messagecorrelationid: 'BB54DC8E-BB02-49A5-9806-4A2433031AA7', - Message: '{"messages":{"message":{"messageid":"E2E3A567-307F-4859-91BA-31B1F4522A7B","messagecorrelationid":"BB54DC8E-BB02-49A5-9806-4A2433031AA7","messagetext":"Betygen är publicerade.","messagesubject":"Betyg klara","messagetime":"2020-12-18T15:59:43.195","linkbackurl":"https://elevdokumentation.stockholm.se/loa3/gradesStudent.do","sender":{"name":"Elevdokumentation"},"recipient":{"recipient":"195709227283","role":"Guardian"},"messagetype":{"type":"webnotify"},"system":"Elevdokumentation","participant":"BB7DE89D-D714-4EB2-85CD-36F9991E7C34"}}}', + Message: + '{"messages":{"message":{"messageid":"E2E3A567-307F-4859-91BA-31B1F4522A7B","messagecorrelationid":"BB54DC8E-BB02-49A5-9806-4A2433031AA7","messagetext":"Betygen är publicerade.","messagesubject":"Betyg klara","messagetime":"2020-12-18T15:59:43.195","linkbackurl":"https://elevdokumentation.stockholm.se/loa3/gradesStudent.do","sender":{"name":"Elevdokumentation"},"recipient":{"recipient":"195709227283","role":"Guardian"},"messagetype":{"type":"webnotify"},"system":"Elevdokumentation","participant":"BB7DE89D-D714-4EB2-85CD-36F9991E7C34"}}}', Readreceipt: false, Recipient: '195709227283', Id: 5880387, DateCreated: '2020-12-18T15:59:46.34', DateModified: '/Date(1608307186340)/', Role: 'Guardian', - Participant: 'BB7DE89D-D714-4EB2-85CD-36F9991E7C34' + Participant: 'BB7DE89D-D714-4EB2-85CD-36F9991E7C34', }, NotificationMessage: { Messages: { Message: { Messageid: 'E2E3A567-307F-4859-91BA-31B1F4522A7B', - Messagecorrelationid: 'BB54DC8E-BB02-49A5-9806-4A2433031AA7', + Messagecorrelationid: + 'BB54DC8E-BB02-49A5-9806-4A2433031AA7', Messagetext: 'Betygen är publicerade.', Messagetime: '/Date(1608303583195)/', - Linkbackurl: 'https://elevdokumentation.stockholm.se/loa3/gradesStudent.do', + Linkbackurl: + 'https://elevdokumentation.stockholm.se/loa3/gradesStudent.do', Category: null, Sender: { Name: 'Elevdokumentation' }, Recipient: { RecipientRecipient: '195709227283', Role: 'Guardian', - Schooltype: null + Schooltype: null, }, Messagetype: { Type: 'webnotify' }, - System: 'Elevdokumentation' - } - } - } + System: 'Elevdokumentation', + }, + }, + }, }, ], } }) it('parses notifications correctly', () => { - expect(parse.notifications(response)).toEqual([{ - id: 'E2E3A567-307F-4859-91BA-31B1F4522A7B', - message: 'Betygen är publicerade.', - sender: 'Elevdokumentation', - url: 'https://elevdokumentation.stockholm.se/loa3/gradesStudent.do', - dateCreated: '2020-12-18T15:59:46.340+01:00', - category: null, - type: 'webnotify', - }]) + expect(parse.notifications(response)).toEqual([ + { + id: 'E2E3A567-307F-4859-91BA-31B1F4522A7B', + message: 'Betygen är publicerade.', + sender: 'Elevdokumentation', + url: 'https://elevdokumentation.stockholm.se/loa3/gradesStudent.do', + dateCreated: '2020-12-18T15:59:46.340+01:00', + category: null, + type: 'webnotify', + }, + ]) }) }) }) diff --git a/lib/parse.ts b/lib/parse.ts index 5d276ce..87300ac 100644 --- a/lib/parse.ts +++ b/lib/parse.ts @@ -1,7 +1,16 @@ import { DateTime, DateTimeOptions } from 'luxon' import { toMarkdown } from './parseHtml' import { - CalendarItem, Child, Classmate, Guardian, MenuItem, NewsItem, ScheduleItem, User, Notification, MenuList, + CalendarItem, + Child, + Classmate, + Guardian, + MenuItem, + NewsItem, + ScheduleItem, + User, + Notification, + MenuList, } from './types' const camel = require('camelcase-keys') @@ -12,7 +21,11 @@ const dateTimeOptions: DateTimeOptions = { zone: 'Europe/Stockholm', } -const tryParseDate = (date: string, format: string, options: DateTimeOptions) => { +const tryParseDate = ( + date: string, + format: string, + options: DateTimeOptions +) => { try { return DateTime.fromFormat(date, format, options).toISO() } catch (err) { @@ -34,7 +47,12 @@ export const etjanst = (response: EtjanstResponse): any | any[] => { } export const user = ({ - socialSecurityNumber, isAuthenticated, userFirstName, userLastName, userEmail, notificationId, + socialSecurityNumber, + isAuthenticated, + userFirstName, + userLastName, + userEmail, + notificationId, }: any): User => ({ personalNumber: socialSecurityNumber, firstName: userFirstName, @@ -44,15 +62,21 @@ export const user = ({ notificationId, }) -export const child = ({ - id, sdsId, name, status, schoolId, -}: any): Child => ({ - id, sdsId, name, status, schoolId, +export const child = ({ id, sdsId, name, status, schoolId }: any): Child => ({ + id, + sdsId, + name, + status, + schoolId, }) export const children = (data: any): Child[] => etjanst(data).map(child) export const guardian = ({ - emailhome, firstname, lastname, address, telmobile, + emailhome, + firstname, + lastname, + address, + telmobile, }: any): Guardian => ({ firstname, lastname, @@ -62,7 +86,11 @@ export const guardian = ({ }) export const classmate = ({ - sisId, firstname, lastname, className, guardians = [], + sisId, + firstname, + lastname, + className, + guardians = [], }: any): Classmate => ({ sisId, firstname, @@ -70,26 +98,51 @@ export const classmate = ({ className, guardians: guardians.map(guardian), }) -export const classmates = (data: any): Classmate[] => etjanst(data).map(classmate) +export const classmates = (data: any): Classmate[] => + etjanst(data).map(classmate) export const calendarItem = ({ - id, title, description, location, longEventDateTime, longEndDateTime, allDayEvent, + id, + title, + description, + location, + longEventDateTime, + longEndDateTime, + allDayEvent, }: any): CalendarItem => ({ id, title, description, location, allDay: allDayEvent, - startDate: longEventDateTime ? DateTime.fromSQL(longEventDateTime, dateTimeOptions).toISO() : undefined, - endDate: longEndDateTime ? DateTime.fromSQL(longEndDateTime, dateTimeOptions).toISO() : undefined, + startDate: longEventDateTime + ? DateTime.fromSQL(longEventDateTime, dateTimeOptions).toISO() + : undefined, + endDate: longEndDateTime + ? DateTime.fromSQL(longEndDateTime, dateTimeOptions).toISO() + : undefined, }) -export const calendar = (data: any): CalendarItem[] => etjanst(data).map(calendarItem) +export const calendar = (data: any): CalendarItem[] => + etjanst(data).map(calendarItem) -const IMAGE_HOST = 'https://etjanst.stockholm.se/Vardnadshavare/inloggad2/NewsBanner?url=' +const IMAGE_HOST = + 'https://etjanst.stockholm.se/Vardnadshavare/inloggad2/NewsBanner?url=' export const newsItem = ({ - newsId, header, preamble, body, bannerImageUrl, pubDateSe, modDateSe, authorDisplayName, altText, + newsId, + header, + preamble, + body, + bannerImageUrl, + pubDateSe, + modDateSe, + authorDisplayName, + altText, }: any): NewsItem => { - const published = tryParseDate(pubDateSe, 'd LLLL yyyy HH:mm', dateTimeOptions) + const published = tryParseDate( + pubDateSe, + 'd LLLL yyyy HH:mm', + dateTimeOptions + ) const modified = tryParseDate(modDateSe, 'd LLLL yyyy HH:mm', dateTimeOptions) return { header, @@ -104,12 +157,20 @@ export const newsItem = ({ body: toMarkdown(body), } } -export const news = (data: any): NewsItem[] => etjanst(data).newsItems.map(newsItem) +export const news = (data: any): NewsItem[] => + etjanst(data).newsItems.map(newsItem) -export const newsItemDetails = (data: any): NewsItem => newsItem(etjanst(data).currentNewsItem) +export const newsItemDetails = (data: any): NewsItem => + newsItem(etjanst(data).currentNewsItem) export const scheduleItem = ({ - title, description, location, longEventDateTime, longEndDateTime, isSameDay, allDayEvent, + title, + description, + location, + longEventDateTime, + longEndDateTime, + isSameDay, + allDayEvent, }: any): ScheduleItem => ({ title, description, @@ -119,28 +180,31 @@ export const scheduleItem = ({ endDate: DateTime.fromSQL(longEndDateTime, dateTimeOptions).toISO(), oneDayEvent: isSameDay, }) -export const schedule = (data: any): ScheduleItem[] => etjanst(data).map(scheduleItem) +export const schedule = (data: any): ScheduleItem[] => + etjanst(data).map(scheduleItem) -export const menuItem = ({ - title, description, -}: any): MenuItem => ({ +export const menuItem = ({ title, description }: any): MenuItem => ({ title, description: toMarkdown(description), }) export const menu = (data: any): MenuItem[] => etjanst(data).map(menuItem) -export const menuList = (data : any) : MenuItem[] => { +export const menuList = (data: any): MenuItem[] => { const etjanstData = etjanst(data) const menuFS = etjanstData as MenuList - const currentWeek = menuFS.menus.find((item) => menuFS.selectedWeek === Number.parseInt(item.week, 10)) + const currentWeek = menuFS.menus.find( + (item) => menuFS.selectedWeek === Number.parseInt(item.week, 10) + ) if (!currentWeek) { - return [{ - title: 'Måndag - Vecka ?', - description: 'Hittade ingen meny', - }] + return [ + { + title: 'Måndag - Vecka ?', + description: 'Hittade ingen meny', + }, + ] } const menuItemsFS = [ @@ -170,22 +234,15 @@ export const menuList = (data : any) : MenuItem[] => { } export const notification = ({ - notification: { - messageid, - dateCreated, - }, + notification: { messageid, dateCreated }, notificationMessage: { messages: { message: { category, messagetext, linkbackurl, - messagetype: { - type, - }, - sender: { - name, - }, + messagetype: { type }, + sender: { name }, }, }, }, @@ -198,4 +255,5 @@ export const notification = ({ category, type, }) -export const notifications = (data: any): Notification[] => etjanst(data).map(notification) +export const notifications = (data: any): Notification[] => + etjanst(data).map(notification) diff --git a/lib/parseHtml.test.ts b/lib/parseHtml.test.ts index a233a27..051319e 100644 --- a/lib/parseHtml.test.ts +++ b/lib/parseHtml.test.ts @@ -78,7 +78,8 @@ describe('parseHtml', () => { expect(toMarkdown(html)).toEqual(expected) }) it('handles real data', () => { - const html = '\u003cdiv data-sp-rte=""\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eHej,\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eNu är det dags för vattenballongkrig \u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e12/2-21 till om med tisdag 16/2-21.\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eAlla knep är tillåtna. \u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cb\u003eDet blir kul.\u003c/b\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eKolla in Reddit\u0026#58; \u003ca href="https\u0026#58;//reddit.com/water-balloons/where-to-buy/" data-cke-saved-href="https\u0026#58;//reddit.com/water-balloons/where-to-buy/"\u003ehttps\u0026#58;//reddit.com/water-balloons/where-to-buy/\u003c/a\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e...och här\u0026#58; \u003ca href="https\u0026#58;//reddit.com/splash-wars/" data-cke-saved-href="https\u0026#58;//reddit.com/splash-wars/"\u003ehttps\u0026#58;//reddit.com/splash-wars/\u003c/a\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003ch2\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eOm att vara hemma vid symtom\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/h2\u003e\u003cp\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eÄven HackerNews är bra.\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003ca href="https\u0026#58;//wnews.ycombinator.com/" data-cke-saved-href="https\u0026#58;//wnews.ycombinator.com/"\u003ehttps\u0026#58;//wnews.ycombinator.com/\u003c/a\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eVi fortsätter också att\u0026#58;\u003c/span\u003e\u003c/span\u003e\u0026#160;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cul\u003e\u003cli style="margin-left\u0026#58;32px;"\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003ehålla avstånd.\u003c/span\u003e\u003c/span\u003e\u0026#160;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/li\u003e\u003cli style="margin-left\u0026#58;32px;"\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eha flera digitala möten.\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/li\u003e\u003cli style="margin-left\u0026#58;32px;"\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003etvätta händerna.\u003c/span\u003e\u003c/span\u003e\u0026#160;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/li\u003e\u003cli style="margin-left\u0026#58;32px;"\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eundvika kollektivtrafik om det är möjligt.\u003c/span\u003e\u003c/span\u003e\u0026#160;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/li\u003e\u003c/ul\u003e\u003cul\u003e\u003cli style="margin-left\u0026#58;32px;"\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003estanna hemma även när man bara känner sig lite sjuk.\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/li\u003e\u003cli style="margin-left\u0026#58;32px;"\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003evädra ofta\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/li\u003e\u003c/ul\u003e\u003cp\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eTa hand om er!\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cb\u003eHa kul tillsammans, på avstånd.\u003c/b\u003e\u0026#160; \u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eStort tack för ert samarbete! \u0026#160;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eVänligen, \u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003erektorfnamn rektorenamn, rektor\u0026#160; \u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cb\u003eVid frågor, kontakta oss gärna\u0026#58; \u003c/b\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cb\u003e\u003cspan\u003eSkolledning\u003c/span\u003e\u003c/b\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003erektorfnamn rektorenamn rektor\u0026#58; \u003c/span\u003e\u003ca href="mailto\u0026#58;rektorfnamn.rektorenamn@edu.stockholm.se" data-cke-saved-href="mailto\u0026#58;rektorfnamn.rektorenamn@edu.stockholm.se"\u003e\u003cspan\u003erektorfnamn.rektorenamn@edu.stockholm.se\u003c/span\u003e\u003c/a\u003e \u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003ebrektorfnamn brektorenamn, bitr. rektor\u0026#58; \u003c/span\u003e\u003ca href="mailto\u0026#58;brektorfnamn.u.brektorenamn@edu.stockholm.se" data-cke-saved-href="mailto\u0026#58;brektorfnamn.u.brektorenamn@edu.stockholm.se"\u003e\u003cspan\u003ebrektorfnamn.u.brektorenamn@edu.stockholm.se\u003c/span\u003e\u003c/a\u003e \u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eb2rektorfnamn b2rektorenamn bitr. rektor\u0026#58; \u003c/span\u003e\u003ca href="mailto\u0026#58;b2rektorfnamn.b2rektorenamn@edu.stockholm.se" data-cke-saved-href="mailto\u0026#58;b2rektorfnamn.b2rektorenamn@edu.stockholm.se"\u003e\u003cspan\u003eb2rektorfnamn.b2rektorenamn@edu.stockholm.se\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cb\u003e\u003cspan\u003eSkolhälsan\u003c/span\u003e\u003c/b\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eb2rektorfnamn skolskstenamn skolsköterska\u0026#58; \u003c/span\u003e\u003ca href="mailto\u0026#58;b2rektorfnamn.skolskstenamn@edu.stockholm.se" data-cke-saved-href="mailto\u0026#58;b2rektorfnamn.skolskstenamn@edu.stockholm.se"\u003e\u003cspan\u003eb2rektorfnamn.skolskstenamn@edu.stockholm.se\u003c/span\u003e\u003c/a\u003e \u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eStort tack för ert samarbete!\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003c/div\u003e\u003cdiv data-sp-rte=""\u003e\u003cp\u003e\u003cbr\u003e\u003c/p\u003e\u003c/div\u003e' + const html = + '\u003cdiv data-sp-rte=""\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eHej,\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eNu är det dags för vattenballongkrig \u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e12/2-21 till om med tisdag 16/2-21.\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eAlla knep är tillåtna. \u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cb\u003eDet blir kul.\u003c/b\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eKolla in Reddit\u0026#58; \u003ca href="https\u0026#58;//reddit.com/water-balloons/where-to-buy/" data-cke-saved-href="https\u0026#58;//reddit.com/water-balloons/where-to-buy/"\u003ehttps\u0026#58;//reddit.com/water-balloons/where-to-buy/\u003c/a\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e...och här\u0026#58; \u003ca href="https\u0026#58;//reddit.com/splash-wars/" data-cke-saved-href="https\u0026#58;//reddit.com/splash-wars/"\u003ehttps\u0026#58;//reddit.com/splash-wars/\u003c/a\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003ch2\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eOm att vara hemma vid symtom\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/h2\u003e\u003cp\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eÄven HackerNews är bra.\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003ca href="https\u0026#58;//wnews.ycombinator.com/" data-cke-saved-href="https\u0026#58;//wnews.ycombinator.com/"\u003ehttps\u0026#58;//wnews.ycombinator.com/\u003c/a\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eVi fortsätter också att\u0026#58;\u003c/span\u003e\u003c/span\u003e\u0026#160;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cul\u003e\u003cli style="margin-left\u0026#58;32px;"\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003ehålla avstånd.\u003c/span\u003e\u003c/span\u003e\u0026#160;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/li\u003e\u003cli style="margin-left\u0026#58;32px;"\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eha flera digitala möten.\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/li\u003e\u003cli style="margin-left\u0026#58;32px;"\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003etvätta händerna.\u003c/span\u003e\u003c/span\u003e\u0026#160;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/li\u003e\u003cli style="margin-left\u0026#58;32px;"\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eundvika kollektivtrafik om det är möjligt.\u003c/span\u003e\u003c/span\u003e\u0026#160;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/li\u003e\u003c/ul\u003e\u003cul\u003e\u003cli style="margin-left\u0026#58;32px;"\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003estanna hemma även när man bara känner sig lite sjuk.\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/li\u003e\u003cli style="margin-left\u0026#58;32px;"\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003evädra ofta\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/li\u003e\u003c/ul\u003e\u003cp\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eTa hand om er!\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cb\u003eHa kul tillsammans, på avstånd.\u003c/b\u003e\u0026#160; \u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eStort tack för ert samarbete! \u0026#160;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eVänligen, \u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003erektorfnamn rektorenamn, rektor\u0026#160; \u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cb\u003eVid frågor, kontakta oss gärna\u0026#58; \u003c/b\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cb\u003e\u003cspan\u003eSkolledning\u003c/span\u003e\u003c/b\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003erektorfnamn rektorenamn rektor\u0026#58; \u003c/span\u003e\u003ca href="mailto\u0026#58;rektorfnamn.rektorenamn@edu.stockholm.se" data-cke-saved-href="mailto\u0026#58;rektorfnamn.rektorenamn@edu.stockholm.se"\u003e\u003cspan\u003erektorfnamn.rektorenamn@edu.stockholm.se\u003c/span\u003e\u003c/a\u003e \u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003ebrektorfnamn brektorenamn, bitr. rektor\u0026#58; \u003c/span\u003e\u003ca href="mailto\u0026#58;brektorfnamn.u.brektorenamn@edu.stockholm.se" data-cke-saved-href="mailto\u0026#58;brektorfnamn.u.brektorenamn@edu.stockholm.se"\u003e\u003cspan\u003ebrektorfnamn.u.brektorenamn@edu.stockholm.se\u003c/span\u003e\u003c/a\u003e \u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eb2rektorfnamn b2rektorenamn bitr. rektor\u0026#58; \u003c/span\u003e\u003ca href="mailto\u0026#58;b2rektorfnamn.b2rektorenamn@edu.stockholm.se" data-cke-saved-href="mailto\u0026#58;b2rektorfnamn.b2rektorenamn@edu.stockholm.se"\u003e\u003cspan\u003eb2rektorfnamn.b2rektorenamn@edu.stockholm.se\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cb\u003e\u003cspan\u003eSkolhälsan\u003c/span\u003e\u003c/b\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eb2rektorfnamn skolskstenamn skolsköterska\u0026#58; \u003c/span\u003e\u003ca href="mailto\u0026#58;b2rektorfnamn.skolskstenamn@edu.stockholm.se" data-cke-saved-href="mailto\u0026#58;b2rektorfnamn.skolskstenamn@edu.stockholm.se"\u003e\u003cspan\u003eb2rektorfnamn.skolskstenamn@edu.stockholm.se\u003c/span\u003e\u003c/a\u003e \u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003e\u003cbr\u003e\u003c/p\u003e\u003cp\u003e\u003cspan\u003e\u003cspan\u003e\u003cspan\u003eStort tack för ert samarbete!\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003c/div\u003e\u003cdiv data-sp-rte=""\u003e\u003cp\u003e\u003cbr\u003e\u003c/p\u003e\u003c/div\u003e' const nbsp = String.fromCharCode(160) const expected = `Hej, diff --git a/lib/parseHtml.ts b/lib/parseHtml.ts index aa053d4..2df064a 100644 --- a/lib/parseHtml.ts +++ b/lib/parseHtml.ts @@ -1,32 +1,15 @@ import * as h2m from 'h2m' import { htmlDecode } from 'js-htmlencode' import { decode } from 'he' -import { - parse, HTMLElement, TextNode, -} from 'node-html-parser' +import { parse, HTMLElement, TextNode } from 'node-html-parser' -const noChildren = [ - 'strong', - 'b', - 'em', - 'i', - 'u', - 's', -] -const trimNodes = [ - ...noChildren, - 'h1', - 'h2', - 'h3', - 'h4', - 'h5', - 'h6', - 'li', - 'a', -] +const noChildren = ['strong', 'b', 'em', 'i', 'u', 's'] +const trimNodes = [...noChildren, 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'li', 'a'] const cleanText = (node: TextNode, parentType: string): TextNode => { - const text = (parentType && trimNodes.includes(parentType.toLowerCase())) - ? node.rawText.trim() : node.rawText + const text = + parentType && trimNodes.includes(parentType.toLowerCase()) + ? node.rawText.trim() + : node.rawText return new TextNode(text) } @@ -44,7 +27,9 @@ const deepClean = (node: HTMLElement): HTMLElement => { node.childNodes.forEach((childNode) => { if (childNode instanceof HTMLElement) { if (node.tagName && noChildren.includes(node.tagName.toLowerCase())) { - cleaned.childNodes.push(cleanText(new TextNode(childNode.innerText), node.tagName)) + cleaned.childNodes.push( + cleanText(new TextNode(childNode.innerText), node.tagName) + ) } else { cleaned.childNodes.push(deepClean(childNode)) } @@ -55,9 +40,8 @@ const deepClean = (node: HTMLElement): HTMLElement => { return cleaned } -export const clean = (html: string = ''): string => ( +export const clean = (html: string = ''): string => deepClean(parse(decode(html))).outerHTML -) interface Node { name: string diff --git a/lib/routes.ts b/lib/routes.ts index 351e9f7..2a40a5f 100644 --- a/lib/routes.ts +++ b/lib/routes.ts @@ -1,69 +1,64 @@ -export const login = (personalNumber: string) => ( +export const login = (personalNumber: string) => `https://login003.stockholm.se/NECSadcmbid/authenticate/NECSadcmbid?TARGET=-SM-HTTPS%3a%2f%2flogin001%2estockholm%2ese%2fNECSadc%2fmbid%2fb64startpage%2ejsp%3fstartpage%3daHR0cHM6Ly9ldGphbnN0LnN0b2NraG9sbS5zZS92YXJkbmFkc2hhdmFyZS9pbmxvZ2dhZDIvaGVt&initialize=bankid&personalNumber=${personalNumber}&_=${Date.now()}` -) -export const loginStatus = (order: string) => ( +export const loginStatus = (order: string) => `https://login003.stockholm.se/NECSadcmbid/authenticate/NECSadcmbid?TARGET=-SM-HTTPS%3a%2f%2flogin001%2estockholm%2ese%2fNECSadc%2fmbid%2fb64startpage%2ejsp%3fstartpage%3daHR0cHM6Ly9ldGphbnN0LnN0b2NraG9sbS5zZS92YXJkbmFkc2hhdmFyZS9pbmxvZ2dhZDIvaGVt&verifyorder=${order}&_=${Date.now()}` -) -export const loginCookie = 'https://login003.stockholm.se/NECSadcmbid/authenticate/SiteMinderAuthADC?TYPE=33554433&REALMOID=06-42f40edd-0c5b-4dbc-b714-1be1e907f2de&GUID=&SMAUTHREASON=0&METHOD=GET&SMAGENTNAME=IfNE0iMOtzq2TcxFADHylR6rkmFtwzoxRKh5nRMO9NBqIxHrc38jFyt56FASdxk1&TARGET=-SM-HTTPS%3a%2f%2flogin001%2estockholm%2ese%2fNECSadc%2fmbid%2fb64startpage%2ejsp%3fstartpage%3daHR0cHM6Ly9ldGphbnN0LnN0b2NraG9sbS5zZS92YXJkbmFkc2hhdmFyZS9pbmxvZ2dhZDIvR2V0Q2hpbGRyZW4%3d' +export const loginCookie = + 'https://login003.stockholm.se/NECSadcmbid/authenticate/SiteMinderAuthADC?TYPE=33554433&REALMOID=06-42f40edd-0c5b-4dbc-b714-1be1e907f2de&GUID=&SMAUTHREASON=0&METHOD=GET&SMAGENTNAME=IfNE0iMOtzq2TcxFADHylR6rkmFtwzoxRKh5nRMO9NBqIxHrc38jFyt56FASdxk1&TARGET=-SM-HTTPS%3a%2f%2flogin001%2estockholm%2ese%2fNECSadc%2fmbid%2fb64startpage%2ejsp%3fstartpage%3daHR0cHM6Ly9ldGphbnN0LnN0b2NraG9sbS5zZS92YXJkbmFkc2hhdmFyZS9pbmxvZ2dhZDIvR2V0Q2hpbGRyZW4%3d' -export const children = 'https://etjanst.stockholm.se/vardnadshavare/inloggad2/GetChildren' +export const children = + 'https://etjanst.stockholm.se/vardnadshavare/inloggad2/GetChildren' -export const calendar = (childId: string) => ( +export const calendar = (childId: string) => `https://etjanst.stockholm.se/vardnadshavare/inloggad2/Calender/GetSchoolCalender?childId=${childId}&rowLimit=50` -) -export const classmates = (childId: string) => ( +export const classmates = (childId: string) => `https://etjanst.stockholm.se/vardnadshavare/inloggad2/contacts/GetStudentsByClass?studentId=${childId}` -) -export const user = 'https://etjanst.stockholm.se/vardnadshavare/base/getuserdata' +export const user = + 'https://etjanst.stockholm.se/vardnadshavare/base/getuserdata' -export const news = (childId: string) => ( +export const news = (childId: string) => `https://etjanst.stockholm.se/vardnadshavare/inloggad2/News/GetNewsArchive?bannerImageLimit=5000&childId=${childId}` -) -export const newsDetails = (childId: string, newsId: string) => ( +export const newsDetails = (childId: string, newsId: string) => `https://etjanst.stockholm.se/vardnadshavare/inloggad2/News/GetNewsArticle?newsItemId=${newsId}&childId=${childId}` -) -export const image = (url: string) => ( +export const image = (url: string) => `https://etjanst.stockholm.se/vardnadshavare/inloggad2/NewsBanner?url=${url}` -) -export const notifications = (childId: string) => ( +export const notifications = (childId: string) => `https://etjanst.stockholm.se/vardnadshavare/inloggad2/Overview/GetNotification?childId=${childId}` -) -export const menuRss = (childId: string) => ( +export const menuRss = (childId: string) => `https://etjanst.stockholm.se/vardnadshavare/inloggad2/Matsedel/GetMatsedelRSS?childId=${childId}` -) -export const menuList = (childId: string) => ( +export const menuList = (childId: string) => `https://etjanst.stockholm.se/vardnadshavare/inloggad2/Matsedel/GetMatsedelList?childId=${childId}` -) -export const menuChoice = (childId: string) => ( +export const menuChoice = (childId: string) => `https://etjanst.stockholm.se/vardnadshavare/inloggad2/Matsedel/GetMatsedelChoice?childId=${childId}` -) -export const schedule = (childId: string, fromDate: string, endDate: string) => ( +export const schedule = (childId: string, fromDate: string, endDate: string) => `https://etjanst.stockholm.se/vardnadshavare/inloggad2/Calender/GetSchema?childId=${childId}&startDate=${fromDate}&endDate=${endDate}` -) export const cdn = 'https://etjanst.stockholm.se/vardnadshavare/base/cdn' export const auth = 'https://etjanst.stockholm.se/vardnadshavare/base/auth' -export const startBundle = 'https://etjanst.stockholm.se/vardnadshavare/bundles/start' +export const startBundle = + 'https://etjanst.stockholm.se/vardnadshavare/bundles/start' -export const hemPage = 'https://etjanst.stockholm.se/vardnadshavare/inloggad2/hem' +export const hemPage = + 'https://etjanst.stockholm.se/vardnadshavare/inloggad2/hem' -export const navigationControllerScript = 'https://etjanst.stockholm.se/vardnadshavare/bundles/navigationController' +export const navigationControllerScript = + 'https://etjanst.stockholm.se/vardnadshavare/bundles/navigationController' export const baseEtjanst = 'https://etjanst.stockholm.se' export const childcontrollerScript = `https://etjanst.stockholm.se/vardnadshavare/bundles/childcontroller?v=${Date.now()}` -export const createItemConfig = 'https://raw.githubusercontent.com/kolplattformen/embedded-api/main/config.json' +export const createItemConfig = + 'https://raw.githubusercontent.com/kolplattformen/embedded-api/main/config.json' diff --git a/lib/types.ts b/lib/types.ts index 4bde315..d01edb7 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -20,10 +20,10 @@ export interface CookieManager { export interface RequestInit { headers?: any method?: string - body?: string, + body?: string /** * Set to `manual` to extract redirect headers, `error` to reject redirect */ - redirect?: string, + redirect?: string } export interface Headers { @@ -160,17 +160,17 @@ export interface MenuItem { } export interface MenuList { - selectedWeek: number, + selectedWeek: number menus: MenuListItem[] } export interface MenuListItem { - 'week': string, - 'mon': string, - 'tue': string, - 'wed': string, - 'thu': string, - 'fri': string + week: string + mon: string + tue: string + wed: string + thu: string + fri: string } export interface User { diff --git a/package.json b/package.json index a18a992..95bdef0 100644 --- a/package.json +++ b/package.json @@ -28,12 +28,15 @@ "@typescript-eslint/parser": "^4.10.0", "eslint": "^7.16.0", "eslint-config-airbnb-typescript": "^12.0.0", + "eslint-config-prettier": "^8.1.0", "eslint-plugin-import": "^2.22.1", + "eslint-plugin-prettier": "^3.3.1", "fetch-cookie": "^0.11.0", "https-proxy-agent": "^5.0.0", "jest": "^26.6.3", "node-blob": "^0.0.2", "node-fetch": "^2.6.1", + "prettier": "^2.2.1", "tough-cookie": "^4.0.0", "ts-jest": "^26.4.4", "typescript": "^4.1.3" diff --git a/run.js b/run.js index 2eece35..6c90939 100644 --- a/run.js +++ b/run.js @@ -1,8 +1,11 @@ -function requestLogger(httpModule){ +function requestLogger(httpModule) { var original = httpModule.request - httpModule.request = function(options, callback){ + httpModule.request = function (options, callback) { console.log('-----------------------------------------------') - console.log(options.href||options.proto+"://"+options.host+options.path, options.method) + console.log( + options.href || options.proto + '://' + options.host + options.path, + options.method + ) console.log(options.headers) console.log('-----------------------------------------------') return original(options, callback) @@ -25,17 +28,19 @@ const init = require('./dist').default const [, , personalNumber] = process.argv if (!personalNumber) { - console.error('You must pass in a valid personal number, eg `node run 197001011111`') + console.error( + 'You must pass in a valid personal number, eg `node run 197001011111`' + ) process.exit(1) } function ensureDirectoryExistence(filePath) { - var dirname = path.dirname(filePath); + var dirname = path.dirname(filePath) if (fs.existsSync(dirname)) { - return true; + return true } - ensureDirectoryExistence(dirname); - fs.mkdirSync(dirname); + ensureDirectoryExistence(dirname) + fs.mkdirSync(dirname) } const record = async (info, data) => { @@ -62,10 +67,10 @@ const record = async (info, data) => { break } } else if (info.error) { - const {message, stack} = info.error + const { message, stack } = info.error content.error = { message, - stack + stack, } } await writeFile(filename, JSON.stringify(content, null, 2)) @@ -76,7 +81,6 @@ async function run() { const fetch = fetchCookie(nodeFetch, cookieJar) try { - const api = init(fetch, cookieJar, { record }) const status = await api.login(personalNumber) status.on('PENDING', () => console.log('PENDING')) @@ -84,7 +88,7 @@ async function run() { status.on('ERROR', () => console.error('ERROR')) status.on('OK', () => console.log('OK')) status.on('CANCELLED', () => { - console.log("User cancelled login") + console.log('User cancelled login') process.exit(0) }) @@ -98,7 +102,7 @@ async function run() { console.log('children') const children = await api.getChildren() console.log(children) -/* + /* console.log('calendar') const calendar = await api.getCalendar(children[0]) console.log(calendar) diff --git a/yarn.lock b/yarn.lock index 04e809b..50dcd39 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1730,6 +1730,11 @@ eslint-config-airbnb@18.2.0: object.assign "^4.1.0" object.entries "^1.1.2" +eslint-config-prettier@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.1.0.tgz#4ef1eaf97afe5176e6a75ddfb57c335121abc5a6" + integrity sha512-oKMhGv3ihGbCIimCAjqkdzx2Q+jthoqnXSP+d86M9tptwugycmTFdVR4IpLgq2c4SHifbwO90z2fQ8/Aio73yw== + eslint-import-resolver-node@^0.3.4: version "0.3.4" resolved "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz" @@ -1765,6 +1770,13 @@ eslint-plugin-import@^2.22.1: resolve "^1.17.0" tsconfig-paths "^3.9.0" +eslint-plugin-prettier@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.1.tgz#7079cfa2497078905011e6f82e8dd8453d1371b7" + integrity sha512-Rq3jkcFY8RYeQLgk2cCwuc0P7SEFwDravPhsJZOQ5N4YI4DSg50NyqJ/9gdZHzQlHf8MvafSesbNJCcP/FF6pQ== + dependencies: + prettier-linter-helpers "^1.0.0" + eslint-scope@^5.0.0, eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" @@ -2001,6 +2013,11 @@ fast-deep-equal@^3.1.1: resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + fast-glob@^3.1.1: version "3.2.4" resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz" @@ -3880,6 +3897,18 @@ prelude-ls@~1.1.2: resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" + integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== + pretty-format@^26.0.0, pretty-format@^26.6.2: version "26.6.2" resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz"