-
-
Notifications
You must be signed in to change notification settings - Fork 43
Intégration Skolengo - Installation + Auth #84
base: development
Are you sure you want to change the base?
Changes from all commits
097cfe3
4df3f29
5970d12
b72ea05
f500e84
542d766
4aa5e08
115376d
f584d40
02f3e29
7eb44aa
77c090b
05b4ff1
1fca312
e26fe86
ea4e250
c8dd934
402742e
2d5fa07
68d3c2d
f68bd25
754765b
e196bf9
162ff96
48ff4a9
c9e8af6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
omit=optional | ||
no-optional=true |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
--install.ignore-optional true | ||
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -0,0 +1,92 @@ | ||||
import { School } from 'scolengo-api/types/models/School'; | ||||
import { SkolengoDataProvider } from './SkolengoDataProvider'; | ||||
import { AppContextType } from '../../utils/AppContext'; | ||||
import { | ||||
AuthRequest, | ||||
exchangeCodeAsync, | ||||
resolveDiscoveryAsync, | ||||
} from 'expo-auth-session'; | ||||
import { coolDownAsync, warmUpAsync } from 'expo-web-browser'; | ||||
import { Alert } from 'react-native'; | ||||
import { OID_CLIENT_ID, OID_CLIENT_SECRET, REDIRECT_URI } from 'scolengo-api'; | ||||
|
||||
const skolengoErrorHandler = (err?: Error | any) => { | ||||
if (err instanceof Error) console.error(err); | ||||
Alert.alert( | ||||
'Erreur', | ||||
'Une erreur est survenue lors de la connexion à Skolengo. Veuillez réessayer.' | ||||
); | ||||
return null; | ||||
}; | ||||
|
||||
export const loginSkolengoWorkflow = async ( | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. faut reformatter, ça manque de lignes vide pour séparer les unités logique |
||||
appContext: AppContextType, | ||||
navigation: any, | ||||
school: School, | ||||
skolengoInstance: SkolengoDataProvider | null = null | ||||
) => { | ||||
const disco = await Promise.all([ | ||||
fetch(school.emsOIDCWellKnownUrl) | ||||
.then((res) => res.json()) | ||||
.then((res) => res.issuer), | ||||
warmUpAsync(), | ||||
]) | ||||
.then(([issuer]) => resolveDiscoveryAsync(issuer)) | ||||
.catch(skolengoErrorHandler); | ||||
if (!disco) return; | ||||
const authRes = new AuthRequest({ | ||||
clientId: OID_CLIENT_ID, | ||||
clientSecret: OID_CLIENT_SECRET, | ||||
redirectUri: REDIRECT_URI, | ||||
extraParams: { | ||||
scope: 'openid', | ||||
response_type: 'code', | ||||
}, | ||||
usePKCE: false, | ||||
}); | ||||
const res = await authRes.promptAsync(disco).catch(skolengoErrorHandler)!; | ||||
coolDownAsync(); | ||||
if (!res || res?.type !== 'success') return; | ||||
if (!res.params.code) { | ||||
return skolengoErrorHandler(res.error); | ||||
} | ||||
const token = await exchangeCodeAsync( | ||||
{ | ||||
clientId: OID_CLIENT_ID, | ||||
clientSecret: OID_CLIENT_SECRET, | ||||
code: res.params.code, | ||||
redirectUri: REDIRECT_URI, | ||||
}, | ||||
disco | ||||
).catch(skolengoErrorHandler); | ||||
if (!token) return skolengoErrorHandler(); | ||||
//console.log({token}); | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||
Alert.alert( | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pas besoin de faire de joli toasts pour ce qui restera en develop |
||||
'Skolengo : intégration en cours', | ||||
'Veuillez patienter, le processus de connexion à Skolengo à fonctionné.\nMais l\'intégration de Skolengo (NG) n\'est pas encode terminé.\n\nRevenez plus tard.' | ||||
); | ||||
/* // TODO : Créer l'intégration via scolengo-api | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. faudrait finir au moins l'enregistrement du token et de l'école |
||||
return; */ | ||||
/* await Promise.all([ | ||||
AsyncStorage.setItem('service', 'Skolengo'), | ||||
AsyncStorage.setItem('token', 'skolengo'), | ||||
]); | ||||
showMessage({ | ||||
message: 'Connecté avec succès', | ||||
type: 'success', | ||||
icon: 'auto', | ||||
floating: true, | ||||
}); | ||||
if (appContext) { | ||||
const a = new SkolengoDatas(token, school); | ||||
await a.saveToken(disco); | ||||
appContext.dataProvider.init('Skolengo').then(() => { | ||||
appContext.setLoggedIn(true); | ||||
navigation.popToTop(); | ||||
}); | ||||
return true; | ||||
} | ||||
skolengoInstance.school = school; | ||||
skolengoInstance.rtInstance = token; | ||||
return skolengoInstance; */ | ||||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,50 @@ | ||
import AsyncStorage from '@react-native-async-storage/async-storage'; | ||
|
||
class SkolengoPrivateCache { | ||
class CommonCacheUtils { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Common" mais prefixé de SkolengoCache_ et sous un namespace Skolengo ? |
||
static ASYNCSTORAGE_PREFIX = 'SkolengoCache_'; | ||
|
||
static _DATE_ENCODING_CHAR = '@'; | ||
|
||
static validateDateString = (str) => | ||
static validateDateString = (str: string) => | ||
typeof str === 'string' && | ||
new Date(str?.toString()).toString() !== 'Invalid Date' && | ||
str === new Date(str?.toString()).toISOString(); | ||
|
||
static parser = (obj) => | ||
static parser = (obj: any) => | ||
JSON.stringify( | ||
obj, | ||
(_key, value) => | ||
this.validateDateString(value) | ||
? `${this.DATE_ENCODING_CHAR}${value}${this.DATE_ENCODING_CHAR}` | ||
? `${this._DATE_ENCODING_CHAR}${value}${this._DATE_ENCODING_CHAR}` | ||
: value, | ||
2 | ||
); | ||
|
||
static deparser = (str) => | ||
static deparser = (str: string) => | ||
JSON.parse(str, (_key, value) => | ||
typeof value === 'string' && | ||
value.startsWith(this.DATE_ENCODING_CHAR) && | ||
value.endsWith(this.DATE_ENCODING_CHAR) | ||
? new Date(value.replaceAll(this.DATE_ENCODING_CHAR, '')) | ||
value.startsWith(this._DATE_ENCODING_CHAR) && | ||
value.endsWith(this._DATE_ENCODING_CHAR) | ||
? new Date(value.replaceAll(this._DATE_ENCODING_CHAR, '')) | ||
: value | ||
); | ||
} | ||
|
||
export class SkolengoCache { | ||
type CommonCacheItem<T> = | ||
| { | ||
expired: true; | ||
maxDate: number; | ||
data: T; | ||
} | ||
| { | ||
expired: false; | ||
maxDate: number; | ||
data: T; | ||
}; | ||
|
||
type CommonCacheSetItem<T> = Omit<CommonCacheItem<T>, 'expired'>; | ||
|
||
export class SkolengoCommonCache { | ||
static SECOND = 1000; | ||
|
||
static MINUTE = 60 * this.SECOND; | ||
|
@@ -39,55 +53,76 @@ export class SkolengoCache { | |
|
||
static DAY = 24 * this.HOUR; | ||
|
||
static TIMEZONE_OFFSET = new Date().getTimezoneOffset() * this.MINUTE; | ||
|
||
static msToTomorrow() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. jamais référencé? |
||
const now = Date.now(); | ||
const timePassedToday = now % this.DAY; | ||
const timePassedToday = (now % this.DAY) + this.TIMEZONE_OFFSET; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. C'est du différentiel, laisse tout en UTC0 au lieu de te faire chier avec les fuseaux horaires |
||
return this.DAY - timePassedToday; | ||
} | ||
|
||
static setItem(key, value, timeout) { | ||
static setItem<T>(key: CacheKeys, value: T, timeout: number) { | ||
const storedDatas = { | ||
maxDate: new Date().getTime() + timeout, | ||
data: value, | ||
}; | ||
return AsyncStorage.setItem( | ||
`${SkolengoPrivateCache.ASYNCSTORAGE_PREFIX}${key}`, | ||
SkolengoPrivateCache.parser(storedDatas) | ||
`${CommonCacheUtils.ASYNCSTORAGE_PREFIX}${key}`, | ||
CommonCacheUtils.parser(storedDatas) | ||
); | ||
} | ||
|
||
static async setCollectionItem(key, id = '', value, timeout) { | ||
const actualCache = await this.getItem(key, {}); | ||
static async setCollectionItem<T>( | ||
key: CacheKeys, | ||
id: string, | ||
value: T, | ||
timeout: number | ||
) { | ||
if (id?.toString().trim().length === 0) return; | ||
actualCache.data[id] = { value, maxDate: new Date().getTime() + timeout }; | ||
const actualCache = await this.getItem< | ||
Record<string, CommonCacheSetItem<T>> | ||
>(key, {}); | ||
actualCache.data![id] = { | ||
data: value, | ||
maxDate: new Date().getTime() + timeout, | ||
}; | ||
return this.setItem(key, actualCache.data, this.DAY * 30); | ||
} | ||
|
||
static async getItem(key, defaultResponse = null) { | ||
static async getItem<T = null>( | ||
key: CacheKeys, | ||
defaultResponse?: T | ||
): Promise<CommonCacheItem<T>> { | ||
const cachedDatas = await AsyncStorage.getItem( | ||
`${SkolengoPrivateCache.ASYNCSTORAGE_PREFIX}${key}` | ||
`${CommonCacheUtils.ASYNCSTORAGE_PREFIX}${key}` | ||
); | ||
if (cachedDatas === null) | ||
return { | ||
expired: true, | ||
maxDate: 0, | ||
data: defaultResponse, | ||
data: defaultResponse || (null as T), | ||
}; | ||
const value = SkolengoPrivateCache.deparser(cachedDatas); | ||
const value = CommonCacheUtils.deparser(cachedDatas); | ||
return { | ||
expired: value.maxDate < new Date().getTime(), | ||
maxDate: value.maxDate, | ||
data: value.data, | ||
}; | ||
} | ||
|
||
static async getCollectionItem(key, id, defaultResponse = null) { | ||
const cachedDatas = await this.getItem(key, {}); | ||
static async getCollectionItem<T = null>( | ||
key: CacheKeys, | ||
id: string, | ||
defaultResponse?: T | ||
) { | ||
const cachedDatas = await this.getItem< | ||
Record<string, CommonCacheSetItem<T>> | ||
>(key, {}); | ||
if (!cachedDatas?.data || !cachedDatas?.data[id]) | ||
return { | ||
expired: true, | ||
maxDate: 0, | ||
data: defaultResponse, | ||
data: defaultResponse || (null as T), | ||
}; | ||
const value = cachedDatas.data[id]; | ||
return { | ||
|
@@ -97,9 +132,9 @@ export class SkolengoCache { | |
}; | ||
} | ||
|
||
static removeItem(key) { | ||
static removeItem(key: CacheKeys) { | ||
return AsyncStorage.removeItem( | ||
`${SkolengoPrivateCache.ASYNCSTORAGE_PREFIX}${key}` | ||
`${CommonCacheUtils.ASYNCSTORAGE_PREFIX}${key}` | ||
); | ||
} | ||
|
||
|
@@ -120,5 +155,9 @@ export class SkolengoCache { | |
homeworkList: 'homeworkList', | ||
timetable: 'timetable', | ||
recap: 'recap', | ||
}; | ||
} as const; | ||
} | ||
|
||
type _CacheKeys = typeof SkolengoCommonCache.cacheKeys; | ||
|
||
export type CacheKeys = _CacheKeys[keyof _CacheKeys]; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
let SkolengoNotImplementedWarn = false; | ||
export class SkolengoDataProvider { | ||
constructor() { | ||
if (!SkolengoNotImplementedWarn) { | ||
console.warn('SkolengoDataProvider is not implemented'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pas besoin de faire de joli warnings pour ce qui restera en develop |
||
SkolengoNotImplementedWarn = true; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ne pas mélanger les package managers