Skip to content

Commit

Permalink
Use classes when type/interface fails (#187)
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesaorson authored Apr 29, 2024
1 parent ae579bd commit 1cf4ffe
Show file tree
Hide file tree
Showing 16 changed files with 277 additions and 190 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
.DS_store
.clj-kondo/
.lsp/
8 changes: 2 additions & 6 deletions client/src/components/AssignmentEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
<label for="required-sections">Choose required sections:</label>
<select id="required-sections" name="required-sections" class="h-10" v-model="state.requiredSectionIds"
multiple>
<option v-for="(sectionMetadata, id) of sectionIndex" :value="id" class="text-black">{{
castToSectionMetadata(sectionMetadata).name }}</option>
<option v-for="[id, sectionMetadata] of sectionIndex" :value="id" class="text-black">{{
sectionMetadata.name }}</option>
</select>
</div>
<div class="text-white border-white border-2 rounded p-1 pl-2 my-2 mr-12">Id</div>
Expand Down Expand Up @@ -87,8 +87,4 @@ const sectionIndex = await SectionService.getAllAsync(
token: await AuthService.getAccessTokenAsync(auth0, { toast: toast }),
}
);
function castToSectionMetadata(value: [string, SectionMetadata]) {
return (value as unknown) as SectionMetadata;
}
</script>
4 changes: 2 additions & 2 deletions client/src/components/CoursePost.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@

<script setup lang="ts">
import AuthService from '@/services/AuthService';
import CourseEditor, { type CourseEditorState } from '@/components/CourseEditor.vue';
import CourseService from '@/services/CourseService';
import type { Course } from '@/models';
import CourseEditor from '@/components/CourseEditor.vue';
import type { Course, CourseEditorState } from '@/models';
import { reactive } from 'vue';
import { useAuth0 } from '@auth0/auth0-vue';
import { useToast } from 'vue-toastification';
Expand Down
114 changes: 94 additions & 20 deletions client/src/models/index.ts
Original file line number Diff line number Diff line change
@@ -1,70 +1,144 @@
export type Id = string;
export type InputKind = 'text' | 'number' | 'code';

export interface Blog {
id: Id
export type ViewKey = {
key: string
kind: InputKind
height?: number
}

export abstract class ViewModel {
id: Id;

static make<T extends ViewModel>(c: new () => T): T {
return new c();
}
constructor(id: Id) {
this.id = id;
}
}

export class Blog extends ViewModel {
content: string
metadata: BlogMetadata
constructor({id = '', content = '', metadata = new BlogMetadata({})} ) {
super(id);
this.content = content;
this.metadata = metadata;
}
};

export interface BlogIndex {
blogs: Map<Id, BlogMetadata>
};

export interface BlogMetadata {
export class BlogMetadata {
description: string
title: string
constructor({description = '', title = ''}) {
this.description = description;
this.title = title;
}
};

export interface Assignment {
id: Id
export class Assignment extends ViewModel {
problemExplanation: string
metadata: AssignmentMetadata
constructor({id = '', problemExplanation = '', metadata = new AssignmentMetadata({})}) {
super(id);
this.problemExplanation = problemExplanation;
this.metadata = metadata;
}
};

export type AssignmentIndex = Map<Id, AssignmentMetadata>;

export interface AssignmentMetadata {
export class AssignmentMetadata {
description: string
name: string
requiredSectionIds: Id[]
courseId: Id
constructor({description = '', name = '', requiredSectionIds = [], courseId = ''}) {
this.description = description;
this.name = name;
this.requiredSectionIds = requiredSectionIds;
this.courseId = courseId;
}
};

export interface Course {
id: Id
export class Course extends ViewModel {
content: string
metadata: CourseMetadata
templatedContent?: string

constructor({id = '', content = '', metadata = new CourseMetadata({})}) {
super(id);
this.content = content;
this.metadata = metadata;
}
};

export class CourseEditorState {
isEditMode: boolean = false
showPreview: boolean = false
id: string = ''
name: string = ''
description: string = ''
content: string = ''
};

export type CourseIndex = Map<Id, CourseMetadata>;

export interface CourseMetadata {
export class CourseMetadata {
description: string
name: string
constructor({description = '', name = ''}) {
this.description = description;
this.name = name;
}
};

export interface Section {
id: Id
export class Section extends ViewModel {
content: string
difficulty: number
metadata: SectionMetadata

constructor({id = '', content = '', difficulty = 0, metadata = new SectionMetadata({})}) {
super(id);
this.content = content;
this.difficulty = difficulty;
this.metadata = metadata;
}
};

export type SectionIndex = Map<Id, SectionMetadata>;

export interface SectionMetadata {
export class SectionMetadata {
description: string
name: string
courseId: Id
constructor({description = '', name = '', courseId = ''}) {
this.description = description;
this.name = name;
this.courseId = courseId;
}
};

export type UserInfo = {
sub: string,
nickname: string,
name: string,
picture: string,
updated_at: string,
email: string,
email_verified: string,
export class UserInfo {
sub: string
nickname: string
name: string
picture: string
updated_at: string
email: string
email_verified: string
constructor({sub = '', nickname = '', name = '', picture = '', updated_at = '', email = '', email_verified = ''}) {
this.sub = sub;
this.nickname = nickname;
this.name = name;
this.picture = picture;
this.updated_at = updated_at;
this.email = email;
this.email_verified = email_verified;
}
};
32 changes: 25 additions & 7 deletions client/src/services/AssignmentService.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import BlobService from './BlobService';
import HttpServiceV1, { type HttpOptions } from './HttpServiceV1';
import type { Assignment, AssignmentIndex, Id } from '@/models';
import { Assignment, AssignmentMetadata, type AssignmentIndex, type Id, type ViewKey } from '@/models';
import type ModelService from './ModelService';

export default class AssignmentService implements ModelService<Assignment> {
objectViewKeys: ViewKey[] = [
{ key: 'id', kind: 'text' },
];

make(): Assignment {
return new Assignment({});
}

export default class AssignmentService {
static async createAsync(assignment: Assignment, options: HttpOptions = {}): Promise<Assignment> {
try {
return await HttpServiceV1.postAsync<Assignment>('assignment', assignment, options);
return new Assignment(
await HttpServiceV1.postAsync('assignment', assignment, options));
}
catch (err: any) {
options.toast?.error(`Failed to create assignment: ${err}`);
Expand All @@ -15,7 +25,8 @@ export default class AssignmentService {

static async deleteAsync(id: Id, options: HttpOptions = {}): Promise<Assignment> {
try {
return await HttpServiceV1.deleteAsync<Assignment>('assignment', id, options);
return new Assignment(
await HttpServiceV1.deleteAsync('assignment', id, options));
}
catch (err: any) {
options.toast?.error(`Failed to delete assignment: ${err}`);
Expand Down Expand Up @@ -45,7 +56,8 @@ export default class AssignmentService {

static async getAsync(id: Id, options: HttpOptions = {}): Promise<Assignment> {
try {
return await HttpServiceV1.getAsync<Assignment>('assignment', id, options);
return new Assignment(
await HttpServiceV1.getAsync('assignment', id, options));
}
catch (err: any) {
options.toast?.error(`Failed to get assignment: ${err}`);
Expand All @@ -55,7 +67,12 @@ export default class AssignmentService {

static async getAllAsync(options: HttpOptions = {}): Promise<AssignmentIndex> {
try {
return await HttpServiceV1.getAllAsync<AssignmentIndex>('assignment', options);
let index = await HttpServiceV1.getAllAsync('assignment', options) as any;
Object.keys(index).forEach(function(key, _) {
index[key] = new AssignmentMetadata(index[key]);
});
index = new Map<Id, AssignmentMetadata>(Object.entries(index));
return index;
}
catch (err: any) {
options.toast?.error(`Failed to get all assignments: ${err}`);
Expand All @@ -65,7 +82,8 @@ export default class AssignmentService {

static async updateAsync(assignment: Assignment, options: HttpOptions = {}): Promise<Assignment> {
try {
return await HttpServiceV1.putAsync<Assignment>('assignment', assignment, options);
return new Assignment(
await HttpServiceV1.putAsync('assignment', assignment, options));
}
catch (err: any) {
options.toast?.error(`Failed to update assignment: ${err}`);
Expand Down
13 changes: 7 additions & 6 deletions client/src/services/AuthService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import HttpServiceV1, { type HttpOptions } from './HttpServiceV1';
import type { Auth0VueClient, User } from '@auth0/auth0-vue';
import type { UserInfo } from '@/models';
import { UserInfo } from '@/models';
import type { Ref } from 'vue';

export default class AuthService {
Expand All @@ -10,11 +10,12 @@ export default class AuthService {

static async getUserInfoAsync(auth0: Auth0VueClient, options: HttpOptions = {}): Promise<UserInfo> {
try {
return await HttpServiceV1.getAsync<UserInfo>(
'user',
'info',
options,
);
return new UserInfo(
await HttpServiceV1.getAsync(
'user',
'info',
options,
));
}
catch (err: any) {
console.error(err);
Expand Down
1 change: 0 additions & 1 deletion client/src/services/BlobService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { Id } from '@/models';
import HttpServiceV1, { type HttpOptions } from './HttpServiceV1';
import type { ToastInterface } from 'vue-toastification';

export default class BlobService {
static async getPresignedUrlAsync(id: Id, options: HttpOptions = {}): Promise<string> {
Expand Down
24 changes: 20 additions & 4 deletions client/src/services/BlogService.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import type { BlogIndex, Id, ViewKey } from '@/models';
import { Blog, BlogMetadata } from '@/models';
import HttpServiceV1, { type HttpOptions } from './HttpServiceV1';
import type { Blog, BlogIndex, Id } from '@/models';
import type ModelService from './ModelService';

export default class BlogService implements ModelService<Blog> {
objectViewKeys: ViewKey[] = [
{ key: 'id', kind: 'text' },
];

make(): Blog {
return new Blog({});
}

export default class BlogService {
static async getAsync(id: Id, options: HttpOptions = {}): Promise<Blog> {
try {
return await HttpServiceV1.getAsync<Blog>('blog', id, options);
return new Blog(
await HttpServiceV1.getAsync('blog', id, options));
}
catch (err: any) {
options.toast?.error(`Failed to get blog: ${err}`);
Expand All @@ -14,7 +25,12 @@ export default class BlogService {

static async getAllAsync(options: HttpOptions = {}): Promise<BlogIndex> {
try {
return await HttpServiceV1.getAllAsync<BlogIndex>('blog', options);
const index = await HttpServiceV1.getAllAsync('blog', options) as any;
Object.keys(index.blogs).forEach(function(key, _) {
index.blogs[key] = new BlogMetadata(index.blogs[key]);
});
index.blogs = new Map<Id, BlogMetadata>(Object.entries(index.blogs));
return index;
}
catch (err: any) {
options.toast?.error(`Failed to get all blogs: ${err}`);
Expand Down
Loading

0 comments on commit 1cf4ffe

Please sign in to comment.