Integrate graphql in website
This commit is contained in:
parent
577e144d0b
commit
be486b6da6
7 changed files with 4162 additions and 82 deletions
|
@ -15,4 +15,12 @@ module.exports = {
|
||||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
},
|
},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: './src/graphql/generated.ts',
|
||||||
|
rules: {
|
||||||
|
'max-len': ['off']
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
14
website/codegen.yml
Normal file
14
website/codegen.yml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
schema: http://localhost:3000/api/website/graphql
|
||||||
|
documents:
|
||||||
|
- src/graphql/queries/get-prompt-info.ts
|
||||||
|
generates:
|
||||||
|
./src/graphql/generated.ts:
|
||||||
|
plugins:
|
||||||
|
- typescript
|
||||||
|
- typescript-operations
|
||||||
|
- typescript-graphql-request
|
||||||
|
config:
|
||||||
|
avoidOptionals: true
|
||||||
|
hooks:
|
||||||
|
afterAllFileWrite:
|
||||||
|
- eslint --fix
|
3919
website/package-lock.json
generated
3919
website/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -5,16 +5,24 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "vue-cli-service serve",
|
"serve": "vue-cli-service serve",
|
||||||
"build": "vue-cli-service build",
|
"build": "vue-cli-service build",
|
||||||
"lint": "vue-cli-service lint"
|
"lint": "vue-cli-service lint",
|
||||||
|
"generate": "graphql-codegen"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
|
"graphql": "^15.4.0",
|
||||||
|
"graphql-request": "^3.4.0",
|
||||||
|
"graphql-tag": "^2.11.0",
|
||||||
"vue": "^2.6.11",
|
"vue": "^2.6.11",
|
||||||
"vue-class-component": "^7.2.3",
|
"vue-class-component": "^7.2.3",
|
||||||
"vue-property-decorator": "^9.1.2",
|
"vue-property-decorator": "^9.1.2",
|
||||||
"vuetify": "^2.2.11"
|
"vuetify": "^2.2.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@graphql-codegen/cli": "^1.20.0",
|
||||||
|
"@graphql-codegen/typescript": "^1.20.0",
|
||||||
|
"@graphql-codegen/typescript-graphql-request": "^3.0.0",
|
||||||
|
"@graphql-codegen/typescript-operations": "^1.17.13",
|
||||||
"@typescript-eslint/eslint-plugin": "^2.33.0",
|
"@typescript-eslint/eslint-plugin": "^2.33.0",
|
||||||
"@typescript-eslint/parser": "^2.33.0",
|
"@typescript-eslint/parser": "^2.33.0",
|
||||||
"@vue/cli-plugin-babel": "~4.5.0",
|
"@vue/cli-plugin-babel": "~4.5.0",
|
||||||
|
|
115
website/src/graphql/generated.ts
Normal file
115
website/src/graphql/generated.ts
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
import { GraphQLClient } from 'graphql-request';
|
||||||
|
import { print } from 'graphql';
|
||||||
|
import gql from 'graphql-tag';
|
||||||
|
|
||||||
|
export type Maybe<T> = T | null;
|
||||||
|
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
|
||||||
|
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
|
||||||
|
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
|
||||||
|
/** All built-in and custom scalars, mapped to their actual values */
|
||||||
|
export type Scalars = {
|
||||||
|
ID: string;
|
||||||
|
String: string;
|
||||||
|
Boolean: boolean;
|
||||||
|
Int: number;
|
||||||
|
Float: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Query = {
|
||||||
|
__typename?: 'Query';
|
||||||
|
promptInfo: PromptInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type QueryPromptInfoArgs = {
|
||||||
|
promptId: Scalars['String'];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PromptInfo = {
|
||||||
|
__typename?: 'PromptInfo';
|
||||||
|
id: Scalars['String'];
|
||||||
|
scopes: Array<Scalars['String']>;
|
||||||
|
clientId: Scalars['String'];
|
||||||
|
studentsMode: StudentsMode;
|
||||||
|
application: PromptInfoApplication;
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum StudentsMode {
|
||||||
|
None = 'None',
|
||||||
|
One = 'One',
|
||||||
|
Many = 'Many'
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PromptInfoApplication = {
|
||||||
|
__typename?: 'PromptInfoApplication';
|
||||||
|
name: Scalars['String'];
|
||||||
|
iconUrl: Maybe<Scalars['String']>;
|
||||||
|
iconColor: Scalars['String'];
|
||||||
|
verified: Scalars['Boolean'];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Mutation = {
|
||||||
|
__typename?: 'Mutation';
|
||||||
|
login: LoginResult;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MutationLoginArgs = {
|
||||||
|
host: Scalars['String'];
|
||||||
|
password: Scalars['String'];
|
||||||
|
username: Scalars['String'];
|
||||||
|
promptId: Scalars['String'];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LoginResult = {
|
||||||
|
__typename?: 'LoginResult';
|
||||||
|
encryptedPrivateKey: Scalars['String'];
|
||||||
|
students: Array<LoginResultStudent>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LoginResultStudent = {
|
||||||
|
__typename?: 'LoginResultStudent';
|
||||||
|
studentId: Scalars['Int'];
|
||||||
|
name: Scalars['String'];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetPromptInfoQueryVariables = Exact<{
|
||||||
|
promptId: Scalars['String'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type GetPromptInfoQuery = (
|
||||||
|
{ __typename?: 'Query' }
|
||||||
|
& { promptInfo: (
|
||||||
|
{ __typename?: 'PromptInfo' }
|
||||||
|
& Pick<PromptInfo, 'scopes' | 'studentsMode'>
|
||||||
|
& { application: (
|
||||||
|
{ __typename?: 'PromptInfoApplication' }
|
||||||
|
& Pick<PromptInfoApplication, 'name' | 'iconUrl' | 'iconColor' | 'verified'>
|
||||||
|
); }
|
||||||
|
); }
|
||||||
|
);
|
||||||
|
|
||||||
|
export const GetPromptInfoDocument = gql`
|
||||||
|
query GetPromptInfo($promptId: String!) {
|
||||||
|
promptInfo(promptId: $promptId) {
|
||||||
|
scopes
|
||||||
|
studentsMode
|
||||||
|
application {
|
||||||
|
name
|
||||||
|
iconUrl
|
||||||
|
iconColor
|
||||||
|
verified
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export type SdkFunctionWrapper = <T>(action: () => Promise<T>) => Promise<T>;
|
||||||
|
|
||||||
|
const defaultWrapper: SdkFunctionWrapper = (sdkFunction) => sdkFunction();
|
||||||
|
export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper = defaultWrapper) {
|
||||||
|
return {
|
||||||
|
GetPromptInfo(variables: GetPromptInfoQueryVariables, requestHeaders?: Headers): Promise<GetPromptInfoQuery> {
|
||||||
|
return withWrapper(() => client.request<GetPromptInfoQuery>(print(GetPromptInfoDocument), variables, requestHeaders));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export type Sdk = ReturnType<typeof getSdk>;
|
14
website/src/graphql/queries/get-prompt-info.ts
Normal file
14
website/src/graphql/queries/get-prompt-info.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import gql from 'graphql-tag';
|
||||||
|
|
||||||
|
export default gql`query GetPromptInfo($promptId: String!) {
|
||||||
|
promptInfo(promptId: $promptId) {
|
||||||
|
scopes
|
||||||
|
studentsMode
|
||||||
|
application {
|
||||||
|
name
|
||||||
|
iconUrl
|
||||||
|
iconColor
|
||||||
|
verified
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`;
|
|
@ -67,47 +67,66 @@
|
||||||
</transition>
|
</transition>
|
||||||
</v-badge>
|
</v-badge>
|
||||||
</div>
|
</div>
|
||||||
<div class="pt-16">
|
<v-window :value="step">
|
||||||
<h2 class="text-subtitle-1 text--secondary mt-6 mb-2 px-4">
|
<v-window-item :value="1">
|
||||||
Aplikacja
|
<div>
|
||||||
<span class="text--primary">{{ promptInfo.application.name }}</span>
|
<div class="pt-16">
|
||||||
chce uzyskać dostęp do twojego konta VULCAN UONET+ przez Wulkanowy Bridge
|
<h2 class="text-subtitle-1 text--secondary mt-6 mb-2 px-4">
|
||||||
</h2>
|
Aplikacja
|
||||||
<v-subheader>Uprawnienia aplikacji</v-subheader>
|
<span class="text--primary">{{ promptInfo.application.name }}</span>
|
||||||
<v-list subheader>
|
chce uzyskać dostęp do twojego konta VULCAN UONET+ przez Wulkanowy Bridge
|
||||||
<v-list-item v-for="item in scopeItems" :key="item.key">
|
</h2>
|
||||||
<v-list-item-icon>
|
<v-subheader>Uprawnienia aplikacji</v-subheader>
|
||||||
<v-icon>{{ item.icon }}</v-icon>
|
<v-list subheader>
|
||||||
</v-list-item-icon>
|
<v-list-item v-for="item in scopeItems" :key="item.key">
|
||||||
<v-list-item-content>
|
<v-list-item-icon>
|
||||||
<v-list-item-title>
|
<v-icon>{{ item.icon }}</v-icon>
|
||||||
{{ item.title }}
|
</v-list-item-icon>
|
||||||
</v-list-item-title>
|
<v-list-item-content>
|
||||||
<v-list-item-subtitle v-if="item.subtitle !== undefined">
|
<v-list-item-title>
|
||||||
{{ item.subtitle }}
|
{{ item.title }}
|
||||||
</v-list-item-subtitle>
|
</v-list-item-title>
|
||||||
</v-list-item-content>
|
<v-list-item-subtitle v-if="item.subtitle !== undefined">
|
||||||
</v-list-item>
|
{{ item.subtitle }}
|
||||||
</v-list>
|
</v-list-item-subtitle>
|
||||||
<v-alert color="info" text class="mb-2 mx-2">
|
</v-list-item-content>
|
||||||
<span class="font-weight-medium">{{ promptInfo.application.name }}</span>
|
</v-list-item>
|
||||||
nie zobaczy twojego hasła
|
</v-list>
|
||||||
<template #append>
|
<v-alert color="info" text class="mb-2 mx-2">
|
||||||
<!-- TODO: Implement -->
|
<span class="font-weight-medium">{{ promptInfo.application.name }}</span>
|
||||||
<v-btn text color="info">Więcej</v-btn>
|
nie zobaczy twojego hasła
|
||||||
</template>
|
<template #append>
|
||||||
</v-alert>
|
<!-- TODO: Implement -->
|
||||||
<v-divider />
|
<v-btn text color="info">Więcej</v-btn>
|
||||||
</div>
|
</template>
|
||||||
<v-card-actions>
|
</v-alert>
|
||||||
<v-btn color="primary" text outlined :href="denyUrl">
|
<v-divider />
|
||||||
Odmów
|
</div>
|
||||||
</v-btn>
|
<v-card-actions>
|
||||||
<v-spacer />
|
<v-btn color="primary" text outlined :href="denyUrl">
|
||||||
<v-btn color="primary">
|
Odmów
|
||||||
Zaloguj się
|
</v-btn>
|
||||||
</v-btn>
|
<v-spacer />
|
||||||
</v-card-actions>
|
<v-btn color="primary" @click="beginLogin">
|
||||||
|
Dalej
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</div>
|
||||||
|
</v-window-item>
|
||||||
|
<v-window-item :value="2">
|
||||||
|
<div>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-btn color="primary" text outlined @click="goBack">
|
||||||
|
Wróć
|
||||||
|
</v-btn>
|
||||||
|
<v-spacer />
|
||||||
|
<v-btn color="primary">
|
||||||
|
Zaloguj się
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</div>
|
||||||
|
</v-window-item>
|
||||||
|
</v-window>
|
||||||
</v-card>
|
</v-card>
|
||||||
</template>
|
</template>
|
||||||
</v-sheet>
|
</v-sheet>
|
||||||
|
@ -144,6 +163,8 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue } from 'vue-property-decorator';
|
import { Component, Vue } from 'vue-property-decorator';
|
||||||
|
import { GraphQLClient } from 'graphql-request';
|
||||||
|
import { getSdk } from '@/graphql/generated';
|
||||||
|
|
||||||
export enum StudentsMode {
|
export enum StudentsMode {
|
||||||
None = 'none',
|
None = 'none',
|
||||||
|
@ -174,36 +195,39 @@ export default class AuthenticatePromptApp extends Vue {
|
||||||
|
|
||||||
step = 1;
|
step = 1;
|
||||||
|
|
||||||
readonly scopeDescriptions: Record<string, {
|
readonly scopeDescriptions: {
|
||||||
|
key: string;
|
||||||
title: string;
|
title: string;
|
||||||
subtitle?: string;
|
subtitle?: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
}> = {
|
}[] = [
|
||||||
timetable: {
|
{
|
||||||
|
key: 'timetable',
|
||||||
title: 'Plan lekcji',
|
title: 'Plan lekcji',
|
||||||
icon: 'mdi-timetable',
|
icon: 'mdi-timetable',
|
||||||
},
|
},
|
||||||
grades: {
|
{
|
||||||
|
key: 'grades',
|
||||||
title: 'Oceny i punkty',
|
title: 'Oceny i punkty',
|
||||||
subtitle: 'Oceny cząstkowe, końcowe, opisowe oraz punkty',
|
subtitle: 'Oceny cząstkowe, końcowe, opisowe oraz punkty',
|
||||||
icon: 'mdi-numeric-6-box-multiple-outline',
|
icon: 'mdi-numeric-6-box-multiple-outline',
|
||||||
},
|
},
|
||||||
notes: {
|
{
|
||||||
title: 'Uwagi',
|
key: 'notes',
|
||||||
|
title: 'Uwagi i pochwały',
|
||||||
icon: 'mdi-note-text-outline',
|
icon: 'mdi-note-text-outline',
|
||||||
},
|
},
|
||||||
achievements: {
|
{
|
||||||
|
key: 'achievements',
|
||||||
title: 'Osiągnięcia',
|
title: 'Osiągnięcia',
|
||||||
icon: 'mdi-trophy-outline',
|
icon: 'mdi-trophy-outline',
|
||||||
},
|
},
|
||||||
}
|
]
|
||||||
|
|
||||||
get scopeItems() {
|
get scopeItems() {
|
||||||
if (this.promptInfo === null) return undefined;
|
if (this.promptInfo === null) return undefined;
|
||||||
return this.promptInfo.scopes.map((key: string) => ({
|
return this.scopeDescriptions
|
||||||
key,
|
.filter(({ key }) => this.promptInfo?.scopes?.includes(key) ?? false);
|
||||||
...this.scopeDescriptions[key],
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get denyUrl() {
|
get denyUrl() {
|
||||||
|
@ -214,20 +238,20 @@ export default class AuthenticatePromptApp extends Vue {
|
||||||
async loadPromptInfo() {
|
async loadPromptInfo() {
|
||||||
this.promptInfoError = false;
|
this.promptInfoError = false;
|
||||||
this.promptInfo = null;
|
this.promptInfo = null;
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
||||||
if (Math.random() < 0.5) {
|
if (!this.promptId) return;
|
||||||
|
|
||||||
|
const client = new GraphQLClient('/api/website/graphql');
|
||||||
|
const sdk = getSdk(client);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { promptInfo } = await sdk.GetPromptInfo({
|
||||||
|
promptId: this.promptId,
|
||||||
|
});
|
||||||
|
this.promptInfo = promptInfo;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
this.promptInfoError = true;
|
this.promptInfoError = true;
|
||||||
} else {
|
|
||||||
this.promptInfo = {
|
|
||||||
scopes: ['grades', 'timetable', 'notes', 'achievements'],
|
|
||||||
studentsMode: StudentsMode.Many,
|
|
||||||
application: {
|
|
||||||
iconColor: '#f00',
|
|
||||||
iconUrl: null,
|
|
||||||
name: 'Not a fancy app',
|
|
||||||
verified: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,5 +261,13 @@ export default class AuthenticatePromptApp extends Vue {
|
||||||
if (!this.promptId) return;
|
if (!this.promptId) return;
|
||||||
await this.loadPromptInfo();
|
await this.loadPromptInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
goBack() {
|
||||||
|
this.step -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
beginLogin() {
|
||||||
|
this.step = 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Reference in a new issue