Skip to content

Commit

Permalink
feat(creature): add support for parsing of mount and minion tooltips
Browse files Browse the repository at this point in the history
re #22, re #23
  • Loading branch information
ReidWeb committed Dec 26, 2021
1 parent 31dd347 commit ee12e9b
Show file tree
Hide file tree
Showing 18 changed files with 1,591 additions and 14 deletions.
4 changes: 2 additions & 2 deletions docs/api/classes/client_LodestoneClient.default.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Client for interfacing with the Final Fantasy XIV Lodestone.

#### Defined in

[LodestoneClient.ts:53](https://github.com/XIVStats/lodestone/blob/81c10ae/src/client/LodestoneClient.ts#L53)
[LodestoneClient.ts:55](https://github.com/XIVStats/lodestone/blob/31dd347/src/client/LodestoneClient.ts#L55)

## Properties

Expand All @@ -43,4 +43,4 @@ An instance will be generated by default, but as a consumer you can provide your

#### Defined in

[LodestoneClient.ts:49](https://github.com/XIVStats/lodestone/blob/81c10ae/src/client/LodestoneClient.ts#L49)
[LodestoneClient.ts:51](https://github.com/XIVStats/lodestone/blob/31dd347/src/client/LodestoneClient.ts#L51)
19 changes: 19 additions & 0 deletions src/client/LodestoneClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import { ICharacterFetchError } from '../interface/ICharacterFetchError'
import CharacterFetchError from '../errors/CharacterFetchError'
import CharacterFetchTimeoutError from '../errors/CharacterFetchTimeoutError'
import ICharacterSetFetchResult from '../interface/ICharacterSetFetchResult'
import IItem from '../interface/IItem'
import Creature from '../entity/Creature'

export type OnSuccessFunction = (id: number, character?: Character) => void
export type OnErrorFunction = (id: number, error: Error) => void
Expand Down Expand Up @@ -80,6 +82,23 @@ export default class LodestoneClient {
}
}

public async getCharacterMounts(id: number, fetchNames?: boolean): Promise<IItem[]> {
// eslint-disable-next-line no-useless-catch
try {
const response = await this.axiosInstance.get(`/character/${id}/mount/`)
const tooltipUrls = Character.getMountTooltipUrlsFromPage(id, response.data, this.cheerioInstance)
return []
} catch (e) {
throw e
}
}

public async getCreatureToolTip(path: string, fetchName?: string): Promise<Creature> {
const shortenedPath = path.replace('/lodestone', '')
const response = await this.axiosInstance.get(shortenedPath)
return Creature.fromToolTip(response.data, this.cheerioInstance)
}

public async getCharacters(
characterIds: number[],
parallelismLimit: number,
Expand Down
12 changes: 10 additions & 2 deletions src/entity/Character.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import IAttributeMapping from '../interface/IAttributeMapping'
import Class from './Class'
import GearCategory from './GearCategory'
import Level from './Level'
import IItem from '../interface/IItem'

export default class Character implements ICharacter {
constructor(readonly id: number, readonly name: string) {}
Expand Down Expand Up @@ -68,7 +69,7 @@ export default class Character implements ICharacter {

minionIds?: string[] | undefined

mountIds?: string[] | undefined
mounts?: IItem[] | undefined

private static processAttribute($: CheerioAPI, config: string | IAttributeMapping | undefined): string | undefined {
if (typeof config === 'object') {
Expand Down Expand Up @@ -127,7 +128,7 @@ export default class Character implements ICharacter {
category,
name: local$.find('.db-tooltip__item__name').text(),
id,
iLvl: Number(local$.find('.db-tooltip__item__level').text().replace('Item Level', '').trim()),
iLvl: Number(local$.find('.db-tooltip__item__level').text().replace('Creature Level', '').trim()),
},
})
}
Expand Down Expand Up @@ -165,4 +166,11 @@ export default class Character implements ICharacter {

return character
}

static getMountTooltipUrlsFromPage(id: number, data: string, cheerio: CheerioAPI): string[] {
const $ = cheerio.load(data)
return $('.character__mounts > ul > li')
.toArray()
.map((listItem) => listItem.attribs['data-tooltip_href'])
}
}
43 changes: 43 additions & 0 deletions src/entity/Creature.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* MIT License
*
* Copyright (c) 2021 Peter Reid <peter@reidweb.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/

import { CheerioAPI } from 'cheerio'
import IItem from '../interface/IItem'
import CreatureType from './CreatureType'

export default class Creature {
constructor(public item: IItem, public creatureType?: CreatureType, public creatureName?: string) {}

static fromToolTip(data: string, cheerio: CheerioAPI, itemIdOnly?: string): Creature {
const $ = cheerio.load(data)
const typeAsString = $('header')[0].attribs.class.replace('__header', '').toUpperCase()
const type = typeAsString as CreatureType
const creatureName = !itemIdOnly ? $('h4').text() : undefined
const item = $('a')[0]
const itemName = !itemIdOnly ? item.attribs['data-tooltip'] : undefined
const itemId = item.attribs.href.replace('/lodestone/playguide/db/item/', '').replace('/', '')
return new Creature({ name: itemName, id: itemId }, type, creatureName)
}
}
31 changes: 31 additions & 0 deletions src/entity/CreatureType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* MIT License
*
* Copyright (c) 2021 Peter Reid <peter@reidweb.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/

const enum CreatureType {
Mount = 'MOUNT',
Minion = 'MINION',
}

export default CreatureType
25 changes: 25 additions & 0 deletions src/entity/__tests__/Character.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import Cheerio from 'cheerio'
import Character from '../Character'
import Class from '../Class'
import GearCategory from '../GearCategory'
import IItem from '../../interface/IItem'

describe('Character', () => {
describe('when loading character information from HTML', () => {
Expand Down Expand Up @@ -384,4 +385,28 @@ describe('Character', () => {
}
})
})

// describe('when loading character mounts from HTML', () => {
// const expectedMountsOne: IItem[] = []
//
// describe.each([[11886902, "P'tajha Rihll", expectedMountsOne]])(
// 'for character %s - %s',
// (charId, name, expected) => {
// let resultantMounts: IItem[]
//
// beforeAll((done) => {
// readFile(join(__dirname, 'resources', 'mount', `${charId}.html`), 'utf8', (err, data) => {
// jest.setTimeout(10000)
// const testString = Buffer.from(data)
// resultantMounts = Character.getMountTooltipUrlsFromPage(charId, testString.toString(), Cheerio)
// done()
// })
// })
//
// it('base test', () => {
// expect(resultantMounts.length).toEqual(23)
// })
// }
// )
// })
})
76 changes: 76 additions & 0 deletions src/entity/__tests__/Creature.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* MIT License
*
* Copyright (c) 2021 Peter Reid <peter@reidweb.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/

import { readFile } from 'fs'
import { join } from 'path'
import Cheerio from 'cheerio'
import Creature from '../Creature'
import CreatureType from '../CreatureType'

describe('Creature', () => {
describe('given a mount tooltip is being loaded', () => {
const expectedMountOne: Creature = {
creatureName: 'Company Chocobo',
creatureType: CreatureType.Mount,
item: {
id: '85f78cb2a87',
name: 'Chocobo Whistle',
},
}
// /lodestone/character/11886902/mount/tooltip/9045c5c5d5d181ee495f0e76af07d6d93c9f0f13
describe.each([['9045c5c5d5d181ee495f0e76af07d6d93c9f0f13', "P'tajha Rihll", 'Company Chocobo', expectedMountOne]])(
'for tooltip %s - relating to %s - Mount: %s',
(toolTipId, name, mountName, expected) => {
let resultantCreature: Creature
const nonObjectAttributes = Object.entries(expected).filter((pair) => typeof pair[1] !== 'object')
const objectAttributes = Object.entries(expected).filter((pair) => typeof pair[1] === 'object')

beforeAll((done) => {
readFile(join(__dirname, 'resources', 'mount', 'tooltip', `${toolTipId}.html`), 'utf8', (err, data) => {
jest.setTimeout(10000)
const testString = Buffer.from(data)
resultantCreature = Creature.fromToolTip(testString.toString(), Cheerio)
done()
})
})

it.each(nonObjectAttributes)("should evaluate %s as '%s'", (key) => {
// @ts-ignore
expect(resultantCreature[key]).toEqual(expected[key])
})

if (objectAttributes.length > 0) {
describe.each(objectAttributes)('should evaluate %s as object', (key, value) => {
// @ts-ignore
it.each(Object.entries(value))("with key %s equal to '%s'", (lowerKey, lowerValue) => {
// @ts-ignore
expect(resultantCreature[key][lowerKey]).toEqual(lowerValue)
})
})
}
}
)
})
})
2 changes: 1 addition & 1 deletion src/entity/__tests__/resources/11886902.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/entity/__tests__/resources/18001255.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/entity/__tests__/resources/27218992.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/entity/__tests__/resources/28309293.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/entity/__tests__/resources/38531003.html

Large diffs are not rendered by default.

0 comments on commit ee12e9b

Please sign in to comment.