Skip to content
This repository has been archived by the owner on Aug 25, 2024. It is now read-only.

Intégration Skolengo - Installation + Auth #84

Open
wants to merge 26 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
097cfe3
refactor: :recycle: amélioration de la modularité de SelectService
NonozgYtb Mar 17, 2024
4df3f29
feat: :wrench: ajout de skolengo (skoapp-prod)
NonozgYtb Mar 17, 2024
5970d12
feat: :sparkles: maj du login Skolengo
NonozgYtb Mar 17, 2024
b72ea05
feat: :sparkles: ajout de SkolengoCommonCache
NonozgYtb Mar 18, 2024
f500e84
fix: :twisted_rightwards_arrows: merge development > feat/scolengo-ng
NonozgYtb Mar 18, 2024
542d766
style: :art: renommage appctx > appContext
NonozgYtb Mar 18, 2024
4aa5e08
fix: :bug: activation conditionnelle de Skolengo
NonozgYtb Mar 18, 2024
115376d
fix: :poop: rajout d'un point (appContext . dataProvider)
NonozgYtb Mar 18, 2024
f584d40
feat: :construction: blocage de la connexion par Skolengo
NonozgYtb Mar 18, 2024
02f3e29
feat: :construction: ajout d'une classe SkolengoDataProvider (en cons…
NonozgYtb Mar 18, 2024
7eb44aa
feat: :construction: ajout du Workflow d'authentification Skolengo (WIP)
NonozgYtb Mar 18, 2024
77c090b
style: :art: lint - LocateSkolengoEtab
NonozgYtb Mar 18, 2024
05b4ff1
feat: :art: restructuration SettingsScreen pour une meilleure lisibilité
NonozgYtb Mar 18, 2024
1fca312
feat: :coffin: suppression de l'ancienne intégration de Skolengo
NonozgYtb Mar 19, 2024
e26fe86
feat: :sparkles: finalisation authentification Skolengo
NonozgYtb Mar 20, 2024
ea4e250
feat: :coffin: suppression GradeView
NonozgYtb Mar 20, 2024
c8dd934
fix: :rewind: revert d'un deeplink de test
NonozgYtb Mar 20, 2024
402742e
feat: :truck: renommage dossier fetch/NewSkolengo > fetch/Skolengo
NonozgYtb Mar 20, 2024
2d5fa07
fix: :ambulance: fix des caractères manquants
NonozgYtb Mar 21, 2024
68d3c2d
test: :mute: - log du token Skolengo
NonozgYtb Mar 21, 2024
f68bd25
fix: syntax LocateSkolengoEtab.tsx
tom-theret Mar 21, 2024
754765b
inset LocateSkolengoEtab.tsx
tom-theret Mar 21, 2024
e196bf9
fix: syntax SettingsScreen.tsx
tom-theret Mar 21, 2024
162ff96
fix: :fire: suppr. GraveView dans AppStack
NonozgYtb Mar 23, 2024
48ff4a9
feat: :art: simplification du SelectService.tsx + typage
NonozgYtb Mar 23, 2024
c9e8af6
Merge branch 'development' into feat/scolengo-ng
NonozgYtb Mar 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
omit=optional
no-optional=true
1 change: 1 addition & 0 deletions .yarnrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--install.ignore-optional true

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

1 change: 0 additions & 1 deletion App.old.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ import HeaderSelectScreen from './views/Settings/HeaderSelectScreen';
import PaymentScreen from './views/Settings/PaymentScreen';

import GradesScreen from './views/GradesScreenNew';
import GradeView from './views/Grades/GradeView';
import GradesSettings from './views/Grades/GradesSettings';

import NewsScreen from './views/NewsScreen';
Expand Down
1 change: 1 addition & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="papillon"/>
<data android:scheme="skoapp-prod"/>
<data android:scheme="xyz.getpapillon.app"/>
<data android:scheme="exp+papillonvex"/>
</intent-filter>
Expand Down
21 changes: 4 additions & 17 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"expo": {
"name": "Papillon",
"slug": "papillonvex",
"scheme": "papillon",
"scheme": ["papillon", "skoapp-prod"],
"version": "6.6.2",
"orientation": "portrait",
"icon": "./assets/icon.png",
Expand All @@ -14,21 +14,6 @@
"notification": {
"icon": "./assets/icon-small-notif.png"
},
"intentFilters": [
{
"action": "VIEW",
"autoVerify": true,
"data": [
{
"scheme": "skoapp-prod"
}
],
"category": [
"BROWSABLE",
"DEFAULT"
]
}
],
"splash": {
"image": "./assets/launch/splash-light.png",
"resizeMode": "cover",
Expand All @@ -38,7 +23,9 @@
"supportsTablet": true,
"appStoreUrl": "https://apps.apple.com/us/app/papillon-lappli-scolaire/id6477761165",
"bundleIdentifier": "xyz.getpapillon.ios",
"associatedDomains": ["applinks:getpapillon.xyz"],
"associatedDomains": [
"applinks:getpapillon.xyz"
],
"splash": {
"backgroundColor": "#32AB8E",
"image": "./assets/launch/splash-light.png",
Expand Down
92 changes: 92 additions & 0 deletions fetch/Skolengo/SkolengoAuthWorkflow.ts
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 (

Choose a reason for hiding this comment

The 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});

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//console.log({token});

Alert.alert(

Choose a reason for hiding this comment

The 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

Choose a reason for hiding this comment

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

Choose a reason for hiding this comment

The 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;
Expand All @@ -39,55 +53,76 @@ export class SkolengoCache {

static DAY = 24 * this.HOUR;

static TIMEZONE_OFFSET = new Date().getTimezoneOffset() * this.MINUTE;

static msToTomorrow() {

Choose a reason for hiding this comment

The 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;

Choose a reason for hiding this comment

The 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 {
Expand All @@ -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}`
);
}

Expand All @@ -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];
9 changes: 9 additions & 0 deletions fetch/Skolengo/SkolengoDataProvider.ts
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');

Choose a reason for hiding this comment

The 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;
}
}
}
Loading