chore: add prettier (#101)

This commit is contained in:
Rickard Natt och Dag 2021-03-30 17:21:18 +02:00 committed by GitHub
parent 0edbc7ee01
commit 19fd3ddd0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 734 additions and 499 deletions

View File

@ -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,
},
}

10
.prettierrc Normal file
View File

@ -0,0 +1,10 @@
{
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"semi": false,
"singleQuote": true,
"trailingComma": "es5",
"bracketSpacing": true,
"jsxBracketSameLine": false
}

View File

@ -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
}
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
}

View File

@ -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)

View File

@ -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'],
}

View File

@ -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 = {

View File

@ -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()

View File

@ -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 = <T>(data: T): Promise<T> => new Promise((res) => (
setTimeout(() => res(data), 200 + Math.random() * 800)
))
const fakeResponse = <T>(data: T): Promise<T> =>
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<RequestInit> {
public async getSession(
url: string,
options?: RequestInit
): Promise<RequestInit> {
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<void> {
public async setSessionCookie(sessionCookie: string): Promise<void> {
// 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<string> {
private async retrieveAuthToken(
url: string,
authBody: string
): Promise<string> {
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<ScheduleItem[]> {
public async getSchedule(
child: Child,
from: DateTime,
to: DateTime
): Promise<ScheduleItem[]> {
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<string> {
private async getMenuChoice(child: Child): Promise<string> {
const url = routes.menuChoice(child.id)
const session = this.getRequestInit()
const response = await this.fetch('menu-choice', url, session)

View File

@ -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()

View File

@ -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<RNCookies>
clearAll(useWebKit?: boolean): Promise<boolean>
}
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)

View File

@ -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: '<html><head><style>\r\np.MsoNormal, li.MsoNormal, div.MsoNormal {\nmargin:0cm;\nmargin-bottom:.0001pt;\nfont-size:11.0pt;\nfont-family:\'Calibri\',sans-serif;\n}\n\na:link, span.MsoHyperlink {\ncolor:#0563C1;\ntext-decoration:underline;\n}\n\nspan.MsoHyperlinkFollowed {\ncolor:#954F72;\ntext-decoration:underline;\n}\n\nspan.E-postmall17 {\nfont-family:\'Calibri\',sans-serif;\ncolor:windowtext;\n}\n\n.MsoChpDefault {\nfont-family:\'Calibri\',sans-serif;\n}\n\ndiv.WordSection1 {\n}\n\r\n</style></head><body lang=\'SV\' link=\'#0563C1\' vlink=\'#954F72\' style=\'\'><div class=\'WordSection1\'><p class=\'MsoNormal\'>&#160;</p></div></body></html>',
description:
"<html><head><style>\r\np.MsoNormal, li.MsoNormal, div.MsoNormal {\nmargin:0cm;\nmargin-bottom:.0001pt;\nfont-size:11.0pt;\nfont-family:'Calibri',sans-serif;\n}\n\na:link, span.MsoHyperlink {\ncolor:#0563C1;\ntext-decoration:underline;\n}\n\nspan.MsoHyperlinkFollowed {\ncolor:#954F72;\ntext-decoration:underline;\n}\n\nspan.E-postmall17 {\nfont-family:'Calibri',sans-serif;\ncolor:windowtext;\n}\n\n.MsoChpDefault {\nfont-family:'Calibri',sans-serif;\n}\n\ndiv.WordSection1 {\n}\n\r\n</style></head><body lang='SV' link='#0563C1' vlink='#954F72' style=''><div class='WordSection1'><p class='MsoNormal'>&#160;</p></div></body></html>",
location: null,
startDate: '2021-06-11',
endDate: '2021-06-11',
@ -443,7 +450,8 @@ const data: any = {
{
title: 'Fritids stängt',
id: 92,
description: '<html><head><style>\r\np.MsoNormal, li.MsoNormal, div.MsoNormal {\nmargin:0cm;\nmargin-bottom:.0001pt;\nfont-size:11.0pt;\nfont-family:\'Calibri\',sans-serif;\n}\n\na:link, span.MsoHyperlink {\ncolor:#0563C1;\ntext-decoration:underline;\n}\n\nspan.MsoHyperlinkFollowed {\ncolor:#954F72;\ntext-decoration:underline;\n}\n\nspan.E-postmall17 {\nfont-family:\'Calibri\',sans-serif;\ncolor:windowtext;\n}\n\n.MsoChpDefault {\nfont-family:\'Calibri\',sans-serif;\n}\n\ndiv.WordSection1 {\n}\n\r\n</style></head><body lang=\'SV\' link=\'#0563C1\' vlink=\'#954F72\' style=\'\'><div class=\'WordSection1\'><p class=\'MsoNormal\'>&#160;</p></div></body></html>',
description:
"<html><head><style>\r\np.MsoNormal, li.MsoNormal, div.MsoNormal {\nmargin:0cm;\nmargin-bottom:.0001pt;\nfont-size:11.0pt;\nfont-family:'Calibri',sans-serif;\n}\n\na:link, span.MsoHyperlink {\ncolor:#0563C1;\ntext-decoration:underline;\n}\n\nspan.MsoHyperlinkFollowed {\ncolor:#954F72;\ntext-decoration:underline;\n}\n\nspan.E-postmall17 {\nfont-family:'Calibri',sans-serif;\ncolor:windowtext;\n}\n\n.MsoChpDefault {\nfont-family:'Calibri',sans-serif;\n}\n\ndiv.WordSection1 {\n}\n\r\n</style></head><body lang='SV' link='#0563C1' vlink='#954F72' style=''><div class='WordSection1'><p class='MsoNormal'>&#160;</p></div></body></html>",
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 +<br/>Inlagd och senapssill',
description:
'Prinskorv potatis rödbetssallad +<br/>Inlagd och senapssill',
},
{
title: 'Fredag - Vecka 51',
description: 'Avslutning Varmkorv bröd ketchup senap<br/>( F-3 i matsalen från 10:30 )',
description:
'Avslutning Varmkorv bröd ketchup senap<br/>( 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 +<br/>Inlagd och senapssill',
description:
'Prinskorv potatis rödbetssallad +<br/>Inlagd och senapssill',
},
{
title: 'Fredag - Vecka 51',
description: 'Avslutning Varmkorv bröd ketchup senap<br/>( F-3 i matsalen från 10:30 )',
description:
'Avslutning Varmkorv bröd ketchup senap<br/>( 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
)

View File

@ -10,7 +10,10 @@ export interface CallInfo extends RequestInit {
}
export interface FetcherOptions {
record?: (info: CallInfo, data: string | Blob | ArrayBuffer | any) => Promise<void>
record?: (
info: CallInfo,
data: string | Blob | ArrayBuffer | any
) => Promise<void>
}
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<void> => {
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<Response> => {
export default function wrap(
fetch: Fetch,
options: FetcherOptions = {}
): Fetcher {
return async (
name: string,
url: string,
init: RequestInit = { headers: {} }
): Promise<Response> => {
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,
},
}

View File

@ -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)

View File

@ -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<void>
}
@ -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)

View File

@ -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: '<div><div data-sp-canvascontrol="" data-sp-canvasdataversion="1.0" data-sp-controldata="&#123;&quot;controlType&quot;&#58;4,&quot;id&quot;&#58;&quot;1212fc8d-dd6b-408a-8d5d-9f1cc787efbb&quot;,&quot;position&quot;&#58;&#123;&quot;controlIndex&quot;&#58;2,&quot;sectionIndex&quot;&#58;1,&quot;sectionFactor&quot;&#58;12,&quot;zoneIndex&quot;&#58;1,&quot;layoutIndex&quot;&#58;1&#125;,&quot;addedFromPersistedData&quot;&#58;true,&quot;emphasis&quot;&#58;&#123;&#125;&#125;"><div data-sp-rte=""><p>Hej,</p><p>Nu är problemet löst! Alla betyg syns som de ska.&#160;</p><p>God jul!</p></div></div><div data-sp-canvascontrol="" data-sp-canvasdataversion="1.0" data-sp-controldata="&#123;&quot;controlType&quot;&#58;0,&quot;pageSettingsSlice&quot;&#58;&#123;&quot;isDefaultDescription&quot;&#58;true,&quot;isDefaultThumbnail&quot;&#58;true&#125;&#125;"></div></div>',
Body:
'<div><div data-sp-canvascontrol="" data-sp-canvasdataversion="1.0" data-sp-controldata="&#123;&quot;controlType&quot;&#58;4,&quot;id&quot;&#58;&quot;1212fc8d-dd6b-408a-8d5d-9f1cc787efbb&quot;,&quot;position&quot;&#58;&#123;&quot;controlIndex&quot;&#58;2,&quot;sectionIndex&quot;&#58;1,&quot;sectionFactor&quot;&#58;12,&quot;zoneIndex&quot;&#58;1,&quot;layoutIndex&quot;&#58;1&#125;,&quot;addedFromPersistedData&quot;&#58;true,&quot;emphasis&quot;&#58;&#123;&#125;&#125;"><div data-sp-rte=""><p>Hej,</p><p>Nu är problemet löst! Alla betyg syns som de ska.&#160;</p><p>God jul!</p></div></div><div data-sp-canvascontrol="" data-sp-canvasdataversion="1.0" data-sp-controldata="&#123;&quot;controlType&quot;&#58;0,&quot;pageSettingsSlice&quot;&#58;&#123;&quot;isDefaultDescription&quot;&#58;true,&quot;isDefaultThumbnail&quot;&#58;true&#125;&#125;"></div></div>',
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": "<div data-sp-rte=\"\"><p><span><span><span>Kära vårdnadshavare!</span></span></span></p><p><span><span><span>I helgen är det avlusningsdagar! Ta tillfället i akt att luskamma ditt barn </span></span></span></p><p><span><span><span>Du finner all info du behöver på <a href=\"https&#58;//www.1177.se/sjukdomar--besvar/hud-har-och-naglar/harbotten-och-harsackar/huvudloss/\" data-cke-saved-href=\"https&#58;//www.1177.se/sjukdomar--besvar/hud-har-och-naglar/harbotten-och-harsackar/huvudloss/\" data-interception=\"on\" title=\"https&#58;//www.1177.se/sjukdomar--besvar/hud-har-och-naglar/harbotten-och-harsackar/huvudloss/\">1177 hemsida </a></span></span></span><span><span><span></span></span></span></p><p><span><span><span>Trevlig helg!</span></span></span></p><p><span><span><span></span></span></span></p></div>",
"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:
'<div data-sp-rte=""><p><span><span><span>Kära vårdnadshavare!</span></span></span></p><p><span><span><span>I helgen är det avlusningsdagar! Ta tillfället i akt att luskamma ditt barn </span></span></span></p><p><span><span><span>Du finner all info du behöver på <a href="https&#58;//www.1177.se/sjukdomar--besvar/hud-har-och-naglar/harbotten-och-harsackar/huvudloss/" data-cke-saved-href="https&#58;//www.1177.se/sjukdomar--besvar/hud-har-och-naglar/harbotten-och-harsackar/huvudloss/" data-interception="on" title="https&#58;//www.1177.se/sjukdomar--besvar/hud-har-och-naglar/harbotten-och-harsackar/huvudloss/">1177 hemsida </a></span></span></span><span><span><span></span></span></span></p><p><span><span><span>Trevlig helg!</span></span></span></p><p><span><span><span></span></span></span></p></div>',
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 .<br/>Veg färs'
Description: 'Körrfärsrätt .<br/>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',
},
])
})
})
})

View File

@ -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)

File diff suppressed because one or more lines are too long

View File

@ -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

View File

@ -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'

View File

@ -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 {

View File

@ -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"

30
run.js
View File

@ -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)

View File

@ -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"