axios test (#705)
* add version controller * use axios to get version * allow typescript * use version api service
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ErsatzTV.Controllers.Api;
|
||||
|
||||
[ApiController]
|
||||
public class VersionController
|
||||
{
|
||||
private static readonly string Version;
|
||||
|
||||
static VersionController()
|
||||
{
|
||||
Version = Assembly.GetEntryAssembly()?
|
||||
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?
|
||||
.InformationalVersion ?? "unknown";
|
||||
}
|
||||
|
||||
[HttpGet("/api/version")]
|
||||
public string GetVersion() => Version;
|
||||
}
|
||||
+10
-1
@@ -68,10 +68,16 @@ namespace ErsatzTV;
|
||||
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IConfiguration configuration) => Configuration = configuration;
|
||||
public Startup(IConfiguration configuration, IWebHostEnvironment env)
|
||||
{
|
||||
Configuration = configuration;
|
||||
CurrentEnvironment = env;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
private IWebHostEnvironment CurrentEnvironment { get; }
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
BugsnagConfiguration bugsnagConfig = Configuration.GetSection("Bugsnag").Get<BugsnagConfiguration>();
|
||||
@@ -131,7 +137,10 @@ public class Startup
|
||||
options.ImplicitlyValidateChildProperties = true;
|
||||
});
|
||||
|
||||
if (!CurrentEnvironment.IsDevelopment())
|
||||
{
|
||||
services.AddSpaStaticFiles(options => options.RootPath = "wwwroot/v2");
|
||||
}
|
||||
|
||||
services.AddMemoryCache();
|
||||
|
||||
|
||||
Generated
+1060
-48
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@mdi/font": "5.9.55",
|
||||
"axios": "^0.26.1",
|
||||
"core-js": "^3.8.3",
|
||||
"pinia": "^2.0.11",
|
||||
"roboto-fontface": "*",
|
||||
@@ -19,16 +20,21 @@
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.16",
|
||||
"@babel/eslint-parser": "^7.12.16",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"@vue/cli-plugin-babel": "~5.0.0",
|
||||
"@vue/cli-plugin-eslint": "~5.0.0",
|
||||
"@vue/cli-plugin-typescript": "~5.0.0",
|
||||
"@vue/cli-service": "~5.0.0",
|
||||
"@vue/composition-api": "^1.4.9",
|
||||
"@vue/eslint-config-typescript": "^9.1.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-vue": "^8.0.3",
|
||||
"sass": "~1.32.0",
|
||||
"sass-loader": "^10.0.0",
|
||||
"typescript": "~4.5.5",
|
||||
"vue-cli-plugin-vuetify": "~2.4.7",
|
||||
"vue-template-compiler": "^2.6.14",
|
||||
"vuetify-loader": "^1.7.0"
|
||||
@@ -41,14 +47,16 @@
|
||||
"extends": [
|
||||
"plugin:vue/essential",
|
||||
"plugin:prettier/recommended",
|
||||
"eslint:recommended"
|
||||
"eslint:recommended",
|
||||
"@vue/typescript"
|
||||
],
|
||||
"parserOptions": {
|
||||
"parser": "@babel/eslint-parser"
|
||||
"parser": "@typescript-eslint/parser"
|
||||
},
|
||||
"rules": {
|
||||
"prettier/prettier": [
|
||||
"error", {
|
||||
"error",
|
||||
{
|
||||
"endOfLine": "auto",
|
||||
"tabWidth": 4
|
||||
}
|
||||
|
||||
@@ -13,6 +13,9 @@ import { applicationState } from '@/stores/applicationState';
|
||||
|
||||
export default {
|
||||
name: 'SideBarVersion',
|
||||
setup() {
|
||||
applicationState().getVersion();
|
||||
},
|
||||
computed: {
|
||||
...mapState(applicationState, [
|
||||
'currentServerVersion',
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import Vue from 'vue';
|
||||
import Vuetify from 'vuetify/lib/framework';
|
||||
import Vuetify from 'vuetify';
|
||||
import colors from 'vuetify/lib/util/colors';
|
||||
|
||||
Vue.use(Vuetify);
|
||||
@@ -0,0 +1,71 @@
|
||||
import type { AxiosError, AxiosInstance, AxiosResponse } from 'axios';
|
||||
import axios from 'axios';
|
||||
|
||||
export function isAxiosError(value: any): value is AxiosError {
|
||||
return typeof value?.response === 'object';
|
||||
}
|
||||
|
||||
export abstract class AbstractApiService {
|
||||
protected readonly http: AxiosInstance;
|
||||
|
||||
protected constructor(
|
||||
protected readonly path?: string,
|
||||
protected readonly baseURL: string = '/'
|
||||
) {
|
||||
if (path) {
|
||||
this.baseURL += path;
|
||||
}
|
||||
this.http = axios.create({
|
||||
baseURL
|
||||
// ... further stuff, e.g. `withCredentials: true`
|
||||
});
|
||||
this.http.defaults.headers.common['Accept'] =
|
||||
'application/json;charset=UTF-8';
|
||||
this.http.defaults.headers.common['Content-Type'] =
|
||||
'application/json;charset=UTF-8';
|
||||
}
|
||||
|
||||
protected createParams(record: Record<string, any>): URLSearchParams {
|
||||
const params: URLSearchParams = new URLSearchParams();
|
||||
for (const key in record) {
|
||||
if (Object.prototype.hasOwnProperty.call(record, key)) {
|
||||
const value: any = record[key];
|
||||
if (value !== null && value !== undefined) {
|
||||
params.append(key, value);
|
||||
} else {
|
||||
console.debug(
|
||||
`Param key '${key}' was null or undefined and will be ignored`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
protected handleResponse<T>(response: AxiosResponse<T>): T {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
protected handleError(error: unknown): never {
|
||||
if (error instanceof Error) {
|
||||
if (isAxiosError(error)) {
|
||||
if (error.response) {
|
||||
console.log(error.response.data);
|
||||
console.log(error.response.status);
|
||||
console.log(error.response.headers);
|
||||
throw error;
|
||||
} else if (error.request) {
|
||||
// The request was made but no response was received
|
||||
// `error.request` is an instance of XMLHttpRequest in the browser
|
||||
console.log(error.request);
|
||||
throw new Error(error as any);
|
||||
}
|
||||
} else {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
console.log('Error', error.message);
|
||||
throw new Error(error.message);
|
||||
}
|
||||
}
|
||||
throw new Error(error as any);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { AbstractApiService } from './AbstractApiService';
|
||||
|
||||
class VersionApiService extends AbstractApiService {
|
||||
public constructor() {
|
||||
super('/api/version');
|
||||
}
|
||||
|
||||
public version(): Promise<string> {
|
||||
return this.http
|
||||
.get('')
|
||||
.then(this.handleResponse)
|
||||
.catch(this.handleError.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
export const versionApiService: VersionApiService = new VersionApiService();
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import Vue, { VNode } from 'vue';
|
||||
|
||||
declare global {
|
||||
namespace JSX {
|
||||
interface Element extends VNode {}
|
||||
interface ElementClass extends Vue {}
|
||||
interface IntrinsicElements {
|
||||
[elem: string]: any;
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
declare module '*.vue' {
|
||||
import Vue from 'vue';
|
||||
export default Vue;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { versionApiService } from '@/services/VersionService';
|
||||
|
||||
const originURL = `${window.location.origin}`;
|
||||
|
||||
@@ -6,7 +7,7 @@ export const applicationState = defineStore('appState', {
|
||||
state: () => {
|
||||
return {
|
||||
miniMenu: false,
|
||||
currentVersion: '0.4.3-7cd2f9a-docker-nvidia', // Needs to be pulled from API with an action when ready
|
||||
currentVersion: 'unknown',
|
||||
m3uURL: originURL + '/iptv/channels.m3u', // this will need to be fixed for reverse proxies
|
||||
xmlURL: originURL + '/iptv/xmltv.xml', // this will need to be fixed for reverse proxies
|
||||
documentationURL: 'https://ersatztv.org/',
|
||||
@@ -37,6 +38,9 @@ export const applicationState = defineStore('appState', {
|
||||
},
|
||||
disableMiniNavigation() {
|
||||
this.miniMenu = false;
|
||||
},
|
||||
async getVersion() {
|
||||
this.currentVersion = await versionApiService.version();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"strict": true,
|
||||
"allowJs": true,
|
||||
"jsx": "preserve",
|
||||
"moduleResolution": "node",
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"useDefineForClassFields": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"types": [
|
||||
"webpack-env",
|
||||
"vuetify"
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
},
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"scripthost"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"tests/**/*.ts",
|
||||
"tests/**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user