First Idea for the DataStore
This commit is contained in:
parent
b9116b799d
commit
8b31fa15f2
5
client/src/app/core/exceptions.ts
Normal file
5
client/src/app/core/exceptions.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export class ImproperlyConfiguredError extends Error {
|
||||||
|
constructor(m: string) {
|
||||||
|
super(m);
|
||||||
|
}
|
||||||
|
}
|
48
client/src/app/core/models/baseModel.ts
Normal file
48
client/src/app/core/models/baseModel.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
import { DS } from 'app/core/services/DS.service';
|
||||||
|
|
||||||
|
const INVALID_COLLECTION_STRING = 'invalid-collection-string';
|
||||||
|
|
||||||
|
export type ModelId = number | string;
|
||||||
|
|
||||||
|
export abstract class BaseModel {
|
||||||
|
abstract id: ModelId;
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
// Typescript does not allow static and abstract at the same time :(((
|
||||||
|
static getCollectionString(): string {
|
||||||
|
return INVALID_COLLECTION_STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getCheckedCollectionString(): string {
|
||||||
|
const collectionString: string = this.getCollectionString();
|
||||||
|
if (collectionString === INVALID_COLLECTION_STRING) {
|
||||||
|
throw new ImproperlyConfigured(
|
||||||
|
'Invalid collection string: Please override the static getCollectionString method!'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return collectionString;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static _get<T extends BaseModel>(id: ModelId): T | undefined {
|
||||||
|
return DS.get(this.getCheckedCollectionString(), id) as T;
|
||||||
|
}
|
||||||
|
protected static _getAll<T extends BaseModel>(): T[] {
|
||||||
|
return DS.getAll(this.getCheckedCollectionString()) as T[];
|
||||||
|
}
|
||||||
|
protected static _filter<T extends BaseModel>(callback): T[] {
|
||||||
|
return DS.filter(this.getCheckedCollectionString(), callback) as T[];
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract getCollectionString(): string;
|
||||||
|
|
||||||
|
save(): Observable<any> {
|
||||||
|
return DS.save(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(): Observable<any> {
|
||||||
|
return DS.delete(this);
|
||||||
|
}
|
||||||
|
}
|
29
client/src/app/core/models/group.ts
Normal file
29
client/src/app/core/models/group.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { BaseModel } from './baseModel';
|
||||||
|
|
||||||
|
export class Group extends BaseModel {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
permissions: string[];
|
||||||
|
|
||||||
|
constructor(id: number) {
|
||||||
|
super();
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getCollectionString(): string {
|
||||||
|
return 'users/group';
|
||||||
|
}
|
||||||
|
static get(id: number): Group | undefined {
|
||||||
|
return this._get<Group>(id);
|
||||||
|
}
|
||||||
|
static getAll(): Group[] {
|
||||||
|
return this._getAll<Group>();
|
||||||
|
}
|
||||||
|
static filter(callback): Group[] {
|
||||||
|
return this._filter<Group>(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCollectionString(): string {
|
||||||
|
return 'users/group';
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,46 @@
|
|||||||
export class User {
|
import { BaseModel } from './baseModel';
|
||||||
|
|
||||||
|
export class User extends BaseModel {
|
||||||
|
id: number;
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
title: string;
|
||||||
|
first_name: string;
|
||||||
|
last_name: string;
|
||||||
|
structure_level: string;
|
||||||
|
number: string;
|
||||||
|
about_me: string;
|
||||||
|
groups_id: number[];
|
||||||
|
is_present: boolean;
|
||||||
|
is_committee: boolean;
|
||||||
|
email: string;
|
||||||
|
last_email_send?: string;
|
||||||
|
comment: string;
|
||||||
|
is_active: boolean;
|
||||||
|
default_password: string;
|
||||||
|
|
||||||
|
constructor(id: number) {
|
||||||
|
super();
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getCollectionString(): string {
|
||||||
|
return 'users/user';
|
||||||
|
}
|
||||||
|
// Make this static lookup methods typesafe
|
||||||
|
// TODO: I'm not happy about this:
|
||||||
|
// - this has to be done for every model
|
||||||
|
// - this may be extendet, if there are more static functionallities for models.
|
||||||
|
static get(id: number): User | undefined {
|
||||||
|
return this._get<User>(id);
|
||||||
|
}
|
||||||
|
static getAll(): User[] {
|
||||||
|
return this._getAll<User>();
|
||||||
|
}
|
||||||
|
static filter(callback): User[] {
|
||||||
|
return this._filter<User>(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCollectionString(): string {
|
||||||
|
return 'users/user';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
16
client/src/app/core/services/DS.service.spec.ts
Normal file
16
client/src/app/core/services/DS.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { TestBed, inject } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { DS } from './DS.service';
|
||||||
|
|
||||||
|
describe('DS', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [DS]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/*it('should be created', inject([DSService], (DS: DSService) => {
|
||||||
|
expect(DS).toBeTruthy();
|
||||||
|
}));*/
|
||||||
|
// just a static use
|
||||||
|
});
|
90
client/src/app/core/services/DS.service.ts
Normal file
90
client/src/app/core/services/DS.service.ts
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import { Observable, of } from 'rxjs';
|
||||||
|
|
||||||
|
import { ImproperlyConfiguredError } from 'app/core/exceptions';
|
||||||
|
import { BaseModel, ModelId } from 'app/core/models/baseModel';
|
||||||
|
|
||||||
|
interface Collection {
|
||||||
|
[id: number]: BaseModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DataStore {
|
||||||
|
[collectionString: string]: Collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DS {
|
||||||
|
static DS: DataStore;
|
||||||
|
|
||||||
|
private constructor() {} // Just a static class!
|
||||||
|
|
||||||
|
static get(collectionString: string, id: ModelId): BaseModel | undefined {
|
||||||
|
const collection: Collection = DS[collectionString];
|
||||||
|
if (!collection) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const model: BaseModel = collection[id];
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
static getAll(collectionString: string): BaseModel[] {
|
||||||
|
const collection: Collection = DS[collectionString];
|
||||||
|
if (!collection) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return Object.values(collection);
|
||||||
|
}
|
||||||
|
// TODO: type for callback function
|
||||||
|
static filter(collectionString: string, callback): BaseModel[] {
|
||||||
|
return this.getAll(collectionString).filter(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inject(collectionString: string, model: BaseModel): void {
|
||||||
|
if (!model.id) {
|
||||||
|
throw new ImproperlyConfiguredError('The model must have an id!');
|
||||||
|
}
|
||||||
|
if (model.getCollectionString() !== collectionString) {
|
||||||
|
throw new ImproperlyConfiguredError('The model you try to insert has not the right collection string');
|
||||||
|
}
|
||||||
|
if (!DS[collectionString]) {
|
||||||
|
DS[collectionString] = {};
|
||||||
|
}
|
||||||
|
DS[collectionString][model.id] = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
static injectMany(collectionString: string, models: BaseModel[]): void {
|
||||||
|
models.forEach(model => {
|
||||||
|
DS.inject(collectionString, model);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static eject(collectionString: string, id: ModelId) {
|
||||||
|
if (DS[collectionString]) {
|
||||||
|
delete DS[collectionString][id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static ejectMany(collectionString: string, ids: ModelId[]) {
|
||||||
|
ids.forEach(id => {
|
||||||
|
DS.eject(collectionString, id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO remove the any there and in BaseModel.
|
||||||
|
static save(model: BaseModel): Observable<any> {
|
||||||
|
if (!model.id) {
|
||||||
|
throw new ImproperlyConfiguredError('The model must have an id!');
|
||||||
|
}
|
||||||
|
const collectionString: string = model.getCollectionString();
|
||||||
|
// make http request to the server
|
||||||
|
// if this was a success, inject the model into the DS
|
||||||
|
return of();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO remove the any there and in BaseModel.
|
||||||
|
static delete(model: BaseModel): Observable<any> {
|
||||||
|
if (!model.id) {
|
||||||
|
throw new ImproperlyConfiguredError('The model must have an id!');
|
||||||
|
}
|
||||||
|
const collectionString: string = model.getCollectionString();
|
||||||
|
// make http request to the server
|
||||||
|
// if this was a success, eject the model from the DS
|
||||||
|
return of();
|
||||||
|
}
|
||||||
|
}
|
@ -43,9 +43,14 @@ export class AuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//loggins a users. expects a user model
|
//loggins a users. expects a user model
|
||||||
login(user: User): Observable<User | any> {
|
login(username: string, password: string): Observable<User | any> {
|
||||||
|
const user: any = {
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
};
|
||||||
return this.http.post<User>('/users/login/', user, httpOptions).pipe(
|
return this.http.post<User>('/users/login/', user, httpOptions).pipe(
|
||||||
tap(val => {
|
tap(val => {
|
||||||
|
localStorage.setItem('username', val.username);
|
||||||
this.isLoggedIn = true;
|
this.isLoggedIn = true;
|
||||||
//Set the session cookie in local storrage.
|
//Set the session cookie in local storrage.
|
||||||
//TODO needs validation
|
//TODO needs validation
|
||||||
|
@ -8,14 +8,14 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<mat-form-field class="login-full-widht">
|
<mat-form-field class="login-full-widht">
|
||||||
<input matInput placeholder="Username" [(ngModel)]="user.username" name="username" required>
|
<input matInput placeholder="Username" [(ngModel)]="username" name="username" required>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<mat-form-field class="example-full-width">
|
<mat-form-field class="example-full-width">
|
||||||
<input matInput placeholder="Password" [(ngModel)]="user.password" type="password" name="password" required>
|
<input matInput placeholder="Password" [(ngModel)]="password" type="password" name="password" required>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -4,7 +4,6 @@ import { Title } from '@angular/platform-browser';
|
|||||||
import { MatSnackBar } from '@angular/material';
|
import { MatSnackBar } from '@angular/material';
|
||||||
|
|
||||||
import { BaseComponent } from 'app/base.component';
|
import { BaseComponent } from 'app/base.component';
|
||||||
import { User } from 'app/core/models/user';
|
|
||||||
import { AuthService } from 'app/core/services/auth.service';
|
import { AuthService } from 'app/core/services/auth.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -13,10 +12,8 @@ import { AuthService } from 'app/core/services/auth.service';
|
|||||||
styleUrls: ['./login.component.css']
|
styleUrls: ['./login.component.css']
|
||||||
})
|
})
|
||||||
export class LoginComponent extends BaseComponent implements OnInit {
|
export class LoginComponent extends BaseComponent implements OnInit {
|
||||||
user: User = {
|
username = '';
|
||||||
username: '',
|
password = '';
|
||||||
password: ''
|
|
||||||
};
|
|
||||||
info: string;
|
info: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -48,7 +45,7 @@ export class LoginComponent extends BaseComponent implements OnInit {
|
|||||||
//like saving a "logged in state" and real checking the server
|
//like saving a "logged in state" and real checking the server
|
||||||
//if logIn was fine
|
//if logIn was fine
|
||||||
formLogin(): void {
|
formLogin(): void {
|
||||||
this.authService.login(this.user).subscribe(res => {
|
this.authService.login(this.username, this.password).subscribe(res => {
|
||||||
if (res.status === 400) {
|
if (res.status === 400) {
|
||||||
//TODO translate
|
//TODO translate
|
||||||
console.log('res: ', res);
|
console.log('res: ', res);
|
||||||
@ -57,8 +54,6 @@ export class LoginComponent extends BaseComponent implements OnInit {
|
|||||||
// this.toastService.success('Logged in! :)');
|
// this.toastService.success('Logged in! :)');
|
||||||
this.setInfo();
|
this.setInfo();
|
||||||
if (this.authService.isLoggedIn) {
|
if (this.authService.isLoggedIn) {
|
||||||
localStorage.setItem('username', res.user.username);
|
|
||||||
|
|
||||||
// Get the redirect URL from our auth service
|
// Get the redirect URL from our auth service
|
||||||
// If no redirect has been set, use the default
|
// If no redirect has been set, use the default
|
||||||
const redirect = this.authService.redirectUrl ? this.authService.redirectUrl : '/';
|
const redirect = this.authService.redirectUrl ? this.authService.redirectUrl : '/';
|
||||||
|
@ -6,4 +6,5 @@
|
|||||||
<span>{{'Welcome to OpenSlides' | translate}}</span>
|
<span>{{'Welcome to OpenSlides' | translate}}</span>
|
||||||
<br/>
|
<br/>
|
||||||
<p translate [translateParams]="{user: 'Tim'}">Hello user</p>
|
<p translate [translateParams]="{user: 'Tim'}">Hello user</p>
|
||||||
|
<button type="button" (click)="test()">test</button>
|
||||||
</div>
|
</div>
|
@ -2,6 +2,10 @@ import { Component, OnInit } from '@angular/core';
|
|||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
import { BaseComponent } from 'app/base.component';
|
import { BaseComponent } from 'app/base.component';
|
||||||
|
|
||||||
|
// for testing the DS and BaseModel
|
||||||
|
import { User } from 'app/core/models/user';
|
||||||
|
import { DS } from 'app/core/services/DS.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-start',
|
selector: 'app-start',
|
||||||
templateUrl: './start.component.html',
|
templateUrl: './start.component.html',
|
||||||
@ -15,4 +19,22 @@ export class StartComponent extends BaseComponent implements OnInit {
|
|||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
super.setTitle('Start page');
|
super.setTitle('Start page');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test() {
|
||||||
|
// This can be a basic unit test ;)
|
||||||
|
console.log(User.get(1));
|
||||||
|
const user1: User = new User(1);
|
||||||
|
user1.username = 'testuser';
|
||||||
|
const user2: User = new User(2);
|
||||||
|
user2.username = 'testuser2';
|
||||||
|
|
||||||
|
DS.injectMany(User.getCollectionString(), [user1, user2]);
|
||||||
|
console.log(User.getAll());
|
||||||
|
console.log(User.filter(user => user.id === 1));
|
||||||
|
|
||||||
|
DS.eject(User.getCollectionString(), user1.id);
|
||||||
|
console.log(User.getAll());
|
||||||
|
console.log(User.filter(user => user.id === 1));
|
||||||
|
console.log(User.filter(user => user.id === 2));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user