First Idea for the DataStore

This commit is contained in:
FinnStutzenstein 2018-06-30 16:56:18 +02:00
parent b9116b799d
commit 8b31fa15f2
11 changed files with 268 additions and 15 deletions

View File

@ -0,0 +1,5 @@
export class ImproperlyConfiguredError extends Error {
constructor(m: string) {
super(m);
}
}

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

View 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';
}
}

View File

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

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

View 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();
}
}

View File

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

View File

@ -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>
@ -26,4 +26,4 @@
<mat-card-actions> <mat-card-actions>
<button mat-raised-button (click)="formLogin()" color="primary">Login</button> <button mat-raised-button (click)="formLogin()" color="primary">Login</button>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>

View File

@ -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 : '/';

View File

@ -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>
</div> <button type="button" (click)="test()">test</button>
</div>

View File

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