Эх сурвалжийг харах

Pull out common got error handling for generic use. Initial distribution loading (no application state storage yet).

Daniel Scalzi 5 жил өмнө
parent
commit
bc43d842e3

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 250 - 318
package-lock.json


+ 23 - 23
package.json

@@ -31,13 +31,13 @@
   "dependencies": {
     "adm-zip": "^0.4.16",
     "async": "^3.2.0",
-    "discord-rpc": "^3.1.1",
-    "electron-updater": "^4.3.1",
+    "discord-rpc": "^3.1.3",
+    "electron-updater": "^4.3.4",
     "fs-extra": "^9.0.1",
     "github-syntax-dark": "^0.5.0",
-    "got": "^11.5.0",
+    "got": "^11.5.2",
     "jquery": "^3.5.1",
-    "lodash": "^4.17.19",
+    "lodash": "^4.17.20",
     "moment": "^2.27.0",
     "request": "^2.88.2",
     "semver": "^7.3.2",
@@ -50,46 +50,46 @@
     "@babel/preset-react": "^7.10.4",
     "@types/adm-zip": "^0.4.33",
     "@types/async": "^3.2.3",
-    "@types/chai": "^4.2.11",
+    "@types/chai": "^4.2.12",
     "@types/discord-rpc": "^3.0.4",
     "@types/fs-extra": "^9.0.1",
-    "@types/jquery": "^3.5.0",
-    "@types/lodash": "^4.14.157",
-    "@types/mocha": "^8.0.0",
-    "@types/node": "^12.12.50",
-    "@types/react": "^16.9.43",
+    "@types/jquery": "^3.5.1",
+    "@types/lodash": "^4.14.160",
+    "@types/mocha": "^8.0.3",
+    "@types/node": "^12.12.54",
+    "@types/react": "^16.9.46",
     "@types/react-dom": "^16.9.8",
     "@types/react-redux": "^7.1.9",
     "@types/react-transition-group": "^4.4.0",
     "@types/request": "^2.48.5",
     "@types/tar-fs": "^2.0.0",
-    "@types/triple-beam": "^1.3.1",
+    "@types/triple-beam": "^1.3.2",
     "@types/winreg": "^1.2.30",
-    "@typescript-eslint/eslint-plugin": "^3.6.1",
-    "@typescript-eslint/parser": "^3.6.1",
+    "@typescript-eslint/eslint-plugin": "^3.10.0",
+    "@typescript-eslint/parser": "^3.10.0",
     "chai": "^4.2.0",
     "cross-env": "^7.0.2",
-    "electron": "^9.1.0",
-    "electron-builder": "^22.7.0",
-    "electron-devtools-installer": "^3.1.0",
+    "electron": "^9.2.1",
+    "electron-builder": "^22.8.0",
+    "electron-devtools-installer": "^3.1.1",
     "electron-webpack": "^2.8.2",
     "electron-webpack-ts": "^4.0.1",
-    "eslint": "^7.4.0",
-    "eslint-plugin-react": "^7.20.3",
+    "eslint": "^7.7.0",
+    "eslint-plugin-react": "^7.20.6",
     "helios-distribution-types": "1.0.0-pre.1",
-    "mocha": "^8.0.1",
-    "nock": "^13.0.2",
+    "mocha": "^8.1.1",
+    "nock": "^13.0.4",
     "react": "^16.13.0",
     "react-dom": "^16.13.0",
     "react-hot-loader": "^4.12.21",
-    "react-redux": "^7.2.0",
+    "react-redux": "^7.2.1",
     "react-transition-group": "^4.4.1",
     "redux": "^4.0.5",
     "rimraf": "^3.0.2",
     "ts-node": "^8.10.2",
     "tsconfig-paths": "^3.9.0",
-    "typescript": "^3.9.6",
-    "webpack": "^4.43.0"
+    "typescript": "^3.9.7",
+    "webpack": "^4.44.1"
   },
   "repository": {
     "type": "git",

+ 106 - 0
src/common/distribution/distribution.ts

@@ -0,0 +1,106 @@
+import { resolve } from 'path'
+import { Distribution } from 'helios-distribution-types'
+import got from 'got'
+import { LoggerUtil } from 'common/logging/loggerutil'
+import { RestResponse, handleGotError, RestResponseStatus } from 'common/got/RestResponse'
+import { pathExists, readFile, writeFile } from 'fs-extra'
+
+export class DistributionAPI {
+
+    private static readonly logger = LoggerUtil.getLogger('DistributionAPI')
+
+    private readonly REMOTE_URL = 'http://mc.westeroscraft.com/WesterosCraftLauncher/distribution.json'
+
+    private readonly DISTRO_FILE = 'distribution.json'
+    private readonly DISTRO_FILE_DEV = 'distribution_dev.json'
+
+    private readonly DEV_MODE = false // placeholder
+
+    private distroPath: string
+    private distroDevPath: string
+
+    private rawDistribution!: Distribution
+
+    constructor(
+        private launcherDirectory: string
+    ) {
+        this.distroPath = resolve(launcherDirectory, this.DISTRO_FILE)
+        this.distroDevPath = resolve(launcherDirectory, this.DISTRO_FILE_DEV)
+    }
+
+    public async testLoad(): Promise<Distribution> {
+        await this.loadDistribution()
+        return this.rawDistribution
+    }
+
+    protected async loadDistribution(): Promise<void> {
+
+        let distro
+
+        if(!this.DEV_MODE) {
+
+            distro = (await this.pullRemote()).data
+            if(distro == null) {
+                distro = await this.pullLocal(false)
+            } else {
+                this.writeDistributionToDisk(distro)
+            }
+
+        } else {
+            distro = await this.pullLocal(true)
+        }
+
+        if(distro == null) {
+            // TODO Bubble this up nicer
+            throw new Error('FATAL: Unable to load distribution from remote server or local disk.')
+        }
+
+        this.rawDistribution = distro
+    }
+
+    protected async pullRemote(): Promise<RestResponse<Distribution | null>> {
+
+        try {
+
+            const res = await got.get<Distribution>(this.REMOTE_URL, { responseType: 'json' })
+
+            return {
+                data: res.body,
+                responseStatus: RestResponseStatus.SUCCESS
+            }
+
+        } catch(error) {
+
+            return handleGotError('Pull Remote', error, DistributionAPI.logger, () => null)
+
+        }
+        
+    }
+
+    protected async writeDistributionToDisk(distribution: Distribution): Promise<void> {
+        await writeFile(this.distroPath, distribution)
+    }
+
+    protected async pullLocal(dev: boolean): Promise<Distribution | null> {
+        return await this.readDistributionFromFile(!dev ? this.distroPath : this.distroDevPath)
+    }
+
+
+    protected async readDistributionFromFile(path: string): Promise<Distribution | null> {
+
+        if(await pathExists(path)) {
+            const raw = await readFile(path, 'utf-8')
+            try {
+                return JSON.parse(raw)
+            } catch(error) {
+                DistributionAPI.logger.error(`Malformed distribution file at ${path}`)
+                return null
+            }
+        } else {
+            DistributionAPI.logger.error(`No distribution file found at ${path}!`)
+            return null
+        }
+
+    }
+
+}

+ 43 - 0
src/common/got/RestResponse.ts

@@ -0,0 +1,43 @@
+import { RequestError, HTTPError, TimeoutError, ParseError } from 'got'
+import { Logger } from 'winston'
+
+export enum RestResponseStatus {
+    
+    SUCCESS,
+    ERROR
+
+}
+
+export interface RestResponse<T> {
+
+    data: T
+    responseStatus: RestResponseStatus
+    error?: RequestError
+
+}
+
+export function handleGotError<T>(operation: string, error: RequestError, logger: Logger, dataProvider: () => T): RestResponse<T> {
+    const response: RestResponse<T> = {
+        data: dataProvider(),
+        responseStatus: RestResponseStatus.ERROR,
+        error
+    }
+    
+    if(error instanceof HTTPError) {
+        logger.error(`Error during ${operation} request (HTTP Response ${error.response.statusCode})`, error)
+        logger.debug('Response Details:')
+        logger.debug('Body:', error.response.body)
+        logger.debug('Headers:', error.response.headers)
+    } else if(Object.getPrototypeOf(error) instanceof RequestError) {
+        logger.error(`${operation} request recieved no response (${error.code}).`, error)
+    } else if(error instanceof TimeoutError) {
+        logger.error(`${operation} request timed out (${error.timings.phases.total}ms).`)
+    } else if(error instanceof ParseError) {
+        logger.error(`${operation} request recieved unexepected body (Parse Error).`)
+    } else {
+        // CacheError, ReadError, MaxRedirectsError, UnsupportedProtocolError, CancelError
+        logger.error(`Error during ${operation} request.`, error)
+    }
+
+    return response
+}

+ 5 - 1
src/common/logging/loggerutil.ts

@@ -1,8 +1,12 @@
 import { createLogger, format, transports, Logger } from 'winston'
-import { SPLAT } from 'triple-beam'
+import { SPLAT as SPLAT_Symbol } from 'triple-beam'
 import moment from 'moment'
 import { inspect } from 'util'
 
+// Workaround until fixed.
+// https://github.com/winstonjs/logform/issues/111
+const SPLAT = SPLAT_Symbol as unknown as string
+
 export class LoggerUtil {
 
     public static getLogger(label: string): Logger {

+ 87 - 0
src/common/mojang/model/internal/MojangResponse.ts

@@ -0,0 +1,87 @@
+import { RestResponse } from 'common/got/RestResponse'
+
+/**
+ * @see https://wiki.vg/Authentication#Errors
+ */
+export enum MojangErrorCode {
+    ERROR_METHOD_NOT_ALLOWED,       // INTERNAL
+    ERROR_NOT_FOUND,                // INTERNAL
+    ERROR_USER_MIGRATED,
+    ERROR_INVALID_CREDENTIALS,
+    ERROR_RATELIMIT,
+    ERROR_INVALID_TOKEN,
+    ERROR_ACCESS_TOKEN_HAS_PROFILE, // ??
+    ERROR_CREDENTIALS_ARE_NULL,     // INTERNAL
+    ERROR_INVALID_SALT_VERSION,     // ??
+    ERROR_UNSUPPORTED_MEDIA_TYPE,   // INTERNAL
+    UNKNOWN
+}
+
+export interface MojangResponse<T> extends RestResponse<T> {
+    mojangErrorCode?: MojangErrorCode
+    isInternalError?: boolean
+}
+
+export interface MojangErrorBody {
+    error: string
+    errorMessage: string
+    cause?: string
+}
+
+/**
+ * Resolve the error response code from the response body.
+ * 
+ * @param body The mojang error body response.
+ */
+export function decipherErrorCode(body: MojangErrorBody): MojangErrorCode {
+
+    if(body.error === 'Method Not Allowed') {
+        return MojangErrorCode.ERROR_METHOD_NOT_ALLOWED
+    } else if(body.error === 'Not Found') {
+        return MojangErrorCode.ERROR_NOT_FOUND
+    } else if(body.error === 'Unsupported Media Type') {
+        return MojangErrorCode.ERROR_UNSUPPORTED_MEDIA_TYPE
+    } else if(body.error === 'ForbiddenOperationException') {
+
+        if(body.cause && body.cause === 'UserMigratedException') {
+            return MojangErrorCode.ERROR_USER_MIGRATED
+        }
+
+        if(body.errorMessage === 'Invalid credentials. Invalid username or password.') {
+            return MojangErrorCode.ERROR_INVALID_CREDENTIALS
+        } else if(body.errorMessage === 'Invalid credentials.') {
+            return MojangErrorCode.ERROR_RATELIMIT
+        } else if(body.errorMessage === 'Invalid token.') {
+            return MojangErrorCode.ERROR_INVALID_TOKEN
+        }
+
+    } else if(body.error === 'IllegalArgumentException') {
+
+        if(body.errorMessage === 'Access token already has a profile assigned.') {
+            return MojangErrorCode.ERROR_ACCESS_TOKEN_HAS_PROFILE
+        } else if(body.errorMessage === 'credentials is null') {
+            return MojangErrorCode.ERROR_CREDENTIALS_ARE_NULL
+        } else if(body.errorMessage === 'Invalid salt version') {
+            return MojangErrorCode.ERROR_INVALID_SALT_VERSION
+        }
+
+    }
+
+    return MojangErrorCode.UNKNOWN
+
+}
+
+// These indicate problems with the code and not the data.
+export function isInternalError(errorCode: MojangErrorCode): boolean {
+    switch(errorCode) {
+        case MojangErrorCode.ERROR_METHOD_NOT_ALLOWED:       // We've sent the wrong method to an endpoint. (ex. GET to POST)
+        case MojangErrorCode.ERROR_NOT_FOUND:                // Indicates endpoint has changed. (404)
+        case MojangErrorCode.ERROR_ACCESS_TOKEN_HAS_PROFILE: // Selecting profiles isn't implemented yet. (Shouldnt happen)
+        case MojangErrorCode.ERROR_CREDENTIALS_ARE_NULL:     // Username/password was not submitted. (UI should forbid this)
+        case MojangErrorCode.ERROR_INVALID_SALT_VERSION:     // ??? (Shouldnt happen)
+        case MojangErrorCode.ERROR_UNSUPPORTED_MEDIA_TYPE:   // Data was not submitted as application/json
+            return true
+        default:
+            return false
+    }
+}

+ 0 - 87
src/common/mojang/model/internal/Response.ts

@@ -1,87 +0,0 @@
-import { RequestError } from 'got'
-
-/**
- * @see https://wiki.vg/Authentication#Errors
- */
-export enum MojangResponseCode {
-    SUCCESS,
-    ERROR,
-    ERROR_METHOD_NOT_ALLOWED, // INTERNAL
-    ERROR_NOT_FOUND, // INTERNAL
-    ERROR_USER_MIGRATED,
-    ERROR_INVALID_CREDENTIALS,
-    ERROR_RATELIMIT,
-    ERROR_INVALID_TOKEN,
-    ERROR_ACCESS_TOKEN_HAS_PROFILE, // ??
-    ERROR_CREDENTIALS_ARE_NULL, // INTERNAL
-    ERROR_INVALID_SALT_VERSION, // ??
-    ERROR_UNSUPPORTED_MEDIA_TYPE // INTERNAL
-}
-
-export interface MojangResponse<T> {
-
-    data: T
-    responseCode: MojangResponseCode
-    error?: RequestError
-    isInternalError?: boolean
-
-}
-
-export interface MojangErrorBody {
-    error: string
-    errorMessage: string
-    cause?: string
-}
-
-export function deciperResponseCode(body: MojangErrorBody): MojangResponseCode {
-
-    if(body.error === 'Method Not Allowed') {
-        return MojangResponseCode.ERROR_METHOD_NOT_ALLOWED
-    } else if(body.error === 'Not Found') {
-        return MojangResponseCode.ERROR_NOT_FOUND
-    } else if(body.error === 'Unsupported Media Type') {
-        return MojangResponseCode.ERROR_UNSUPPORTED_MEDIA_TYPE
-    } else if(body.error === 'ForbiddenOperationException') {
-
-        if(body.cause && body.cause === 'UserMigratedException') {
-            return MojangResponseCode.ERROR_USER_MIGRATED
-        }
-
-        if(body.errorMessage === 'Invalid credentials. Invalid username or password.') {
-            return MojangResponseCode.ERROR_INVALID_CREDENTIALS
-        } else if(body.errorMessage === 'Invalid credentials.') {
-            return MojangResponseCode.ERROR_RATELIMIT
-        } else if(body.errorMessage === 'Invalid token.') {
-            return MojangResponseCode.ERROR_INVALID_TOKEN
-        }
-
-    } else if(body.error === 'IllegalArgumentException') {
-
-        if(body.errorMessage === 'Access token already has a profile assigned.') {
-            return MojangResponseCode.ERROR_ACCESS_TOKEN_HAS_PROFILE
-        } else if(body.errorMessage === 'credentials is null') {
-            return MojangResponseCode.ERROR_CREDENTIALS_ARE_NULL
-        } else if(body.errorMessage === 'Invalid salt version') {
-            return MojangResponseCode.ERROR_INVALID_SALT_VERSION
-        }
-
-    }
-
-    return MojangResponseCode.ERROR
-
-}
-
-// These indicate problems with the code and not the data.
-export function isInternalError(responseCode: MojangResponseCode): boolean {
-    switch(responseCode) {
-        case MojangResponseCode.ERROR_METHOD_NOT_ALLOWED:       // We've sent the wrong method to an endpoint. (ex. GET to POST)
-        case MojangResponseCode.ERROR_NOT_FOUND:                // Indicates endpoint has changed. (404)
-        case MojangResponseCode.ERROR_ACCESS_TOKEN_HAS_PROFILE: // Selecting profiles isn't implemented yet. (Shouldnt happen)
-        case MojangResponseCode.ERROR_CREDENTIALS_ARE_NULL:     // Username/password was not submitted. (UI should forbid this)
-        case MojangResponseCode.ERROR_INVALID_SALT_VERSION:     // ??? (Shouldnt happen)
-        case MojangResponseCode.ERROR_UNSUPPORTED_MEDIA_TYPE:   // Data was not submitted as application/json
-            return true
-        default:
-            return false
-    }
-}

+ 16 - 30
src/common/mojang/mojang.ts

@@ -1,10 +1,11 @@
 import { LoggerUtil } from '../logging/loggerutil'
 import { Agent } from './model/auth/Agent'
 import { Status, StatusColor } from './model/internal/Status'
-import got, { RequestError, HTTPError, TimeoutError, ParseError } from 'got'
+import got, { RequestError, HTTPError } from 'got'
 import { Session } from './model/auth/Session'
 import { AuthPayload } from './model/auth/AuthPayload'
-import { MojangResponse, MojangResponseCode, deciperResponseCode, isInternalError, MojangErrorBody } from './model/internal/Response'
+import { MojangResponse, MojangErrorCode, decipherErrorCode, isInternalError, MojangErrorBody } from './model/internal/MojangResponse'
+import { RestResponseStatus, handleGotError } from 'common/got/RestResponse'
 
 export class Mojang {
 
@@ -90,31 +91,16 @@ export class Mojang {
     }
 
     private static handleGotError<T>(operation: string, error: RequestError, dataProvider: () => T): MojangResponse<T> {
-        const response: MojangResponse<T> = {
-            data: dataProvider(),
-            responseCode: MojangResponseCode.ERROR,
-            error
-        }
-        
+
+        const response: MojangResponse<T> = handleGotError(operation, error, Mojang.logger, dataProvider)
+
         if(error instanceof HTTPError) {
-            response.responseCode = deciperResponseCode(error.response.body as MojangErrorBody)
-            Mojang.logger.error(`Error during ${operation} request (HTTP Response ${error.response.statusCode})`, error)
-            Mojang.logger.debug('Response Details:')
-            Mojang.logger.debug('Body:', error.response.body)
-            Mojang.logger.debug('Headers:', error.response.headers)
-        } else if(Object.getPrototypeOf(error) instanceof RequestError) {
-            Mojang.logger.error(`${operation} request recieved no response (${error.code}).`, error)
-        } else if(error instanceof TimeoutError) {
-            Mojang.logger.error(`${operation} request timed out (${error.timings.phases.total}ms).`)
-        } else if(error instanceof ParseError) {
-            Mojang.logger.error(`${operation} request recieved unexepected body (Parse Error).`)
+            response.mojangErrorCode = decipherErrorCode(error.response.body as MojangErrorBody)
         } else {
-            // CacheError, ReadError, MaxRedirectsError, UnsupportedProtocolError, CancelError
-            Mojang.logger.error(`Error during ${operation} request.`, error)
+            response.mojangErrorCode = MojangErrorCode.UNKNOWN
         }
-
-        response.isInternalError = isInternalError(response.responseCode)
-
+        response.isInternalError = isInternalError(response.mojangErrorCode)
+    
         return response
     }
 
@@ -151,7 +137,7 @@ export class Mojang {
 
             return {
                 data: Mojang.statuses,
-                responseCode: MojangResponseCode.SUCCESS
+                responseStatus: RestResponseStatus.SUCCESS
             }
 
         } catch(error) {
@@ -201,7 +187,7 @@ export class Mojang {
             Mojang.expectSpecificSuccess('Mojang Authenticate', 200, res.statusCode)
             return {
                 data: res.body,
-                responseCode: MojangResponseCode.SUCCESS
+                responseStatus: RestResponseStatus.SUCCESS
             }
 
         } catch(err) {
@@ -233,14 +219,14 @@ export class Mojang {
 
             return {
                 data: res.statusCode === 204,
-                responseCode: MojangResponseCode.SUCCESS
+                responseStatus: RestResponseStatus.SUCCESS
             }
 
         } catch(err) {
             if(err instanceof HTTPError && err.response.statusCode === 403) {
                 return {
                     data: false,
-                    responseCode: MojangResponseCode.SUCCESS
+                    responseStatus: RestResponseStatus.SUCCESS
                 }
             }
             return Mojang.handleGotError('Mojang Validate', err, () => false)
@@ -271,7 +257,7 @@ export class Mojang {
 
             return {
                 data: undefined,
-                responseCode: MojangResponseCode.SUCCESS
+                responseStatus: RestResponseStatus.SUCCESS
             }
 
         } catch(err) {
@@ -306,7 +292,7 @@ export class Mojang {
 
             return {
                 data: res.body,
-                responseCode: MojangResponseCode.SUCCESS
+                responseStatus: RestResponseStatus.SUCCESS
             }
 
         } catch(err) {

+ 3 - 1
src/main/index.ts

@@ -113,7 +113,9 @@ async function createWindow() {
         webPreferences: {
             preload: join(__dirname, '..', 'out', 'preloader.js'),
             nodeIntegration: true,
-            contextIsolation: false
+            contextIsolation: false,
+            enableRemoteModule: true,
+            worldSafeExecuteJavaScript: true
         },
         backgroundColor: '#171614'
     })

+ 10 - 3
src/renderer/components/Application.tsx

@@ -17,6 +17,8 @@ import { join } from 'path'
 import Overlay from './overlay/Overlay'
 import { OverlayPushAction, OverlayActionDispatch } from '../redux/actions/overlayActions'
 
+import { DistributionAPI } from 'common/distribution/distribution'
+
 import './Application.css'
 
 declare const __static: string
@@ -120,9 +122,14 @@ class Application extends React.Component<ApplicationProps & typeof mapDispatch,
                 setTimeout(() => {
                     //this.props.setView(View.WELCOME)
                     this.props.pushGenericOverlay({
-                        title: 'Test Title',
-                        description: 'Test Description',
-                        dismissible: true
+                        title: 'Load Distribution',
+                        description: 'This is a test. Will load the distribution.',
+                        dismissible: false,
+                        acknowledgeCallback: async () => {
+                            const distro = new DistributionAPI('C:\\Users\\user\\AppData\\Roaming\\Helios Launcher')
+                            const x = await distro.testLoad()
+                            console.log(x)
+                        }
                     })
                     this.props.pushGenericOverlay({
                         title: 'Test Title 2',

+ 6 - 6
src/renderer/components/overlay/generic-overlay/GenericOverlay.tsx

@@ -10,8 +10,8 @@ export interface GenericOverlayProps {
     acknowledgeText?: string
     dismissText?: string
     dismissible: boolean
-    acknowledgeCallback?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void
-    dismissCallback?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void
+    acknowledgeCallback?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => Promise<void>
+    dismissCallback?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => Promise<void>
 }
 
 const mapDispatch = {
@@ -30,16 +30,16 @@ class GenericOverlay extends React.Component<InternalGenericOverlayProps> {
         return this.props.dismissText || 'Dismiss'
     }
 
-    private onAcknowledgeClick = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>): void => {
+    private onAcknowledgeClick = async (event: React.MouseEvent<HTMLButtonElement, MouseEvent>): Promise<void> => {
         if(this.props.acknowledgeCallback) {
-            this.props.acknowledgeCallback(event)
+            await this.props.acknowledgeCallback(event)
         }
         this.props.popOverlayContent()
     }
 
-    private onDismissClick = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>): void => {
+    private onDismissClick = async (event: React.MouseEvent<HTMLButtonElement, MouseEvent>): Promise<void> => {
         if(this.props.dismissCallback) {
-            this.props.dismissCallback(event)
+            await this.props.dismissCallback(event)
         }
         this.props.popOverlayContent()
     }

+ 31 - 14
test/mojang/mojangTest.ts

@@ -1,17 +1,33 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
 import { Mojang } from 'common/mojang/mojang'
 import { expect } from 'chai'
 import nock from 'nock'
 import { Session } from 'common/mojang/model/auth/Session'
-import { MojangResponseCode } from 'common/mojang/model/internal/Response'
+import { MojangErrorCode, MojangResponse } from 'common/mojang/model/internal/MojangResponse'
+import { RestResponseStatus, RestResponse } from 'common/got/RestResponse'
 
-function expectMojangResponse(res: any, responseCode: MojangResponseCode, negate = false) {
+function assertResponse(res: RestResponse<unknown>) {
     expect(res).to.not.be.an('error')
     expect(res).to.be.an('object')
-    expect(res).to.have.property('responseCode')
+}
+
+function expectSuccess(res: RestResponse<unknown>) {
+    assertResponse(res)
+    expect(res).to.have.property('responseStatus')
+    expect(res.responseStatus).to.equal(RestResponseStatus.SUCCESS)
+}
+
+function expectFailure(res: RestResponse<unknown>) {
+    expect(res.responseStatus).to.not.equal(RestResponseStatus.SUCCESS)
+}
+
+function expectMojangResponse(res: MojangResponse<unknown>, responseCode: MojangErrorCode, negate = false) {
+    assertResponse(res)
+    expect(res).to.have.property('mojangErrorCode')
     if(!negate) {
-        expect(res.responseCode).to.equal(responseCode)
+        expect(res.mojangErrorCode).to.equal(responseCode)
     } else {
-        expect(res.responseCode).to.not.equal(responseCode)
+        expect(res.mojangErrorCode).to.not.equal(responseCode)
     }
 }
 
@@ -30,7 +46,7 @@ describe('Mojang Errors', () => {
             .reply(500, 'Service temprarily offline.')
 
         const res = await Mojang.status()
-        expectMojangResponse(res, MojangResponseCode.SUCCESS, true)
+        expectFailure(res)
         expect(res.data).to.be.an('array')
         expect(res.data).to.deep.equal(defStatusHack)
 
@@ -40,7 +56,8 @@ describe('Mojang Errors', () => {
 
         nock(Mojang.AUTH_ENDPOINT)
             .post('/authenticate')
-            .reply(403, (uri, requestBody: any): { error: string, errorMessage: string } => {
+            // eslint-disable-next-line @typescript-eslint/no-unused-vars
+            .reply(403, (uri, requestBody: unknown): { error: string, errorMessage: string } => {
                 return {
                     error: 'ForbiddenOperationException',
                     errorMessage: 'Invalid credentials. Invalid username or password.'
@@ -48,7 +65,7 @@ describe('Mojang Errors', () => {
             })
 
         const res = await Mojang.authenticate('user', 'pass', 'xxx', true)
-        expectMojangResponse(res, MojangResponseCode.ERROR_INVALID_CREDENTIALS)
+        expectMojangResponse(res, MojangErrorCode.ERROR_INVALID_CREDENTIALS)
         expect(res.data).to.be.a('null')
         expect(res.error).to.not.be.a('null')
 
@@ -66,7 +83,7 @@ describe('Mojang Status', () => {
             .reply(200, defStatusHack)
 
         const res = await Mojang.status()
-        expectMojangResponse(res, MojangResponseCode.SUCCESS)
+        expectSuccess(res)
         expect(res.data).to.be.an('array')
         expect(res.data).to.deep.equal(defStatusHack)
 
@@ -101,7 +118,7 @@ describe('Mojang Auth', () => {
             })
 
         const res = await Mojang.authenticate('user', 'pass', 'xxx', true)
-        expectMojangResponse(res, MojangResponseCode.SUCCESS)
+        expectSuccess(res)
         expect(res.data!.clientToken).to.equal('xxx')
         expect(res.data).to.have.property('user')
 
@@ -120,13 +137,13 @@ describe('Mojang Auth', () => {
 
         const res = await Mojang.validate('abc', 'def')
 
-        expectMojangResponse(res, MojangResponseCode.SUCCESS)
+        expectSuccess(res)
         expect(res.data).to.be.a('boolean')
         expect(res.data).to.equal(true)
 
         const res2 = await Mojang.validate('def', 'def')
 
-        expectMojangResponse(res2, MojangResponseCode.SUCCESS)
+        expectSuccess(res2)
         expect(res2.data).to.be.a('boolean')
         expect(res2.data).to.equal(false)
 
@@ -140,7 +157,7 @@ describe('Mojang Auth', () => {
 
         const res = await Mojang.invalidate('adc', 'def')
 
-        expectMojangResponse(res, MojangResponseCode.SUCCESS)
+        expectSuccess(res)
 
     })
 
@@ -169,7 +186,7 @@ describe('Mojang Auth', () => {
             })
 
         const res = await Mojang.refresh('gfd', 'xxx', true)
-        expectMojangResponse(res, MojangResponseCode.SUCCESS)
+        expectSuccess(res)
         expect(res.data!.clientToken).to.equal('xxx')
         expect(res.data).to.have.property('user')
 

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно