Commit e9269472 authored by Laurent's avatar Laurent

Iguana: auth, loans and holds

parent 88ae3c2a
import {PortalAdapter} from './adapter';
import * as cheerio from 'cheerio';
import {Account, Loan, Hold} from '../../models';
import {WrongLoginPassword} from './wrong-login-password';
import {PortalAdapter} from './adapter'
import * as cheerio from 'cheerio'
import * as xmljs from 'xml-js'
import {Account, Loan, Hold} from '../../models'
import {WrongLoginPassword} from './wrong-login-password'
export class IguanaV3 extends PortalAdapter {
public getIdentifier(): string {
return 'iguana-v3';
return 'iguana-v3'
}
public getLabel(): string {
return 'Iguana 3';
return 'Iguana 3'
}
public canHandleWebsite($): boolean {
return $('a[href*="www.main.cls?sUrl="]').length;
return $('a[href*="www.main.cls?sUrl="]').length
}
protected _fetchLoans(account: Account): Promise<Array<Loan>> {
return Promise.resolve(new Array<Loan>());
return this
._soapRequest(account,
{
CurrentLoans: {
SessionId: account.credentials['session_id'],
Data: {
From: 1,
To: 20
}
}
})
.then( (datas: any) => {
let items = datas.CurrentLoansResponse.CurrentLoansResult.Items.Item;
return items.map( (item) => {
let title_parts = /([^\/]+)\/([^;]+)/.exec(item.Title._text);
return new Loan()
.setLoanId(item.Barcode._text)
.setTitle(title_parts[1].trim())
.setAuthor(title_parts[2].trim())
.setDateDue(this._formatDate(item.DueDate._text))
.setLibrary(item.LoanLocation._text.split('^')[0])
.setRecordId(item.Link._text)
.setRecordThumbnail(item.Image._text)
})
})
}
protected _fetchHolds(account: Account): Promise<Array<Hold>> {
return this
._soapRequest(account,
{
CurrentReservations: {
SessionId: account.credentials['session_id'],
Data: {
From: 1,
To: 20
}
}
})
.then( (datas: any) => {
let items = datas.CurrentReservationsResponse.CurrentReservationsResult.Items.Item;
return items.map( (item) => {
let title_parts = /([^:]+):.*\/([^;]+)/.exec(item.Title._text);
return new Hold()
.setHoldId(item.ReservationNumber._text)
.setTitle(title_parts[1].trim())
.setAuthor(title_parts[2].trim())
.setStatus(item.AvailableSince._text ? L('hold_available') : L('hold_not_available'))
.setLibrary(item.PickupLocations.Location.Name._text)
.setRecordId(item.Link._text)
.setRecordThumbnail(item.Image._text)
})
})
}
protected _formatDate(date: string): string {
return [date.substr(0,4),
date.substr(4,2),
date.substr(6,2)].join('-')
}
protected _signIn(account): Promise<any> {
return Promise.resolve();
if (!(account.credentials['login'] && account.credentials['password']))
return Promise.reject(new WrongLoginPassword());
return this
.request(account, { url: account.getUrl() + '/iguana/www.main.cls?surl=accueil' })
.then( (response) => {
let session_regex = /Vfocus.Settings.sessionID = "([a-zA-Z0-9]+)"/
return session_regex.exec(response.content.toString())[1]
})
.then( (csp_session_id) => {
return this
._soapRequest(account,
{
CheckCredentials: {
Language: 'fre',
Profile: 'Iguana',
CspSessionId: csp_session_id,
LogonId: account.credentials['login'],
Password: account.credentials['password']
}
})
.then( (datas) => {
let result = datas['CheckCredentialsResponse']['CheckCredentialsResult']
if ('1' != result['Result']['_text'])
return Promise.reject(new WrongLoginPassword());
account.credentials['session_id'] = result['SessionId']['_text']
account.beConnected();
})
})
}
protected _soapRequest(account: Account, data: object): Promise<object> {
let envelope = '<?xml version="1.0" encoding="UTF-8"?>'
+ '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:xsd="http://www.w3.org/1999/XMLSchema">'
+ '<SOAP-ENV:Body>' + xmljs.js2xml(data, {compact: true}) + '</SOAP-ENV:Body>'
+ '</SOAP-ENV:Envelope>';
return this
.request(account,
{ url: account.getUrl() + '/iguana/Proxy.UserActivities.cls',
method: 'POST',
content: envelope,
headers: {"Content-Type": "text/xml; charset=UTF-8"} })
.then( (response) => {
let datas = xmljs.xml2js(response.content.toString(),
{compact: true,
ignoreAttributes: true});
return datas['SOAP-ENV:Envelope']['SOAP-ENV:Body'];
})
}
}
......@@ -44,7 +44,6 @@ export class KohaCas extends Koha {
username: account.credentials['login'],
password: account.credentials['password'],
execution: form_inputs['execution'],
geolocation: '',
'_eventId': form_inputs['_eventId'],
'lt': form_inputs['lt'] || '',
'submit': form_inputs['submit']
......
declare var expect: any;
import {HTTPScenario} from './http-scenario';
class IguanaV3LoansAndHoldsWithSuccessfulLogin extends HTTPScenario {
export class IguanaV3LoansAndHoldsWithSuccessfulLogin extends HTTPScenario {
public constructor(base_url: string) {
super();
this.setup(base_url);
}
public soapEnvelope(content: string): string {
return '<?xml version="1.0" encoding="UTF-8"?>'
+ '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:xsd="http://www.w3.org/1999/XMLSchema">'
+ '<SOAP-ENV:Body>' + content + '</SOAP-ENV:Body>'
+ '</SOAP-ENV:Envelope>';
}
public setup(base_url:string) {
this.expect({ url: base_url + 'empr.php',
method: "POST",
content: "login=marco&password=polo",
headers: {"Content-Type": "application/x-www-form-urlencoded"} },
{ content: this.frontPageConnected() })
this
.expect({ url: base_url + '/iguana/www.main.cls?surl=accueil' },
{ content: this.homePage() })
.then({ url: base_url + '/iguana/Proxy.UserActivities.cls',
method: 'POST',
content: this.soapEnvelope('<CheckCredentials>'
+ '<Language>fre</Language>'
+ '<Profile>Iguana</Profile>'
+ '<CspSessionId>2pu4GQqnf0</CspSessionId>'
+ '<LogonId>marco</LogonId>'
+ '<Password>secret</Password>'
+ '</CheckCredentials>'),
headers: {"Content-Type": "text/xml; charset=UTF-8"} },
{ content: this.checkCredentialsResponse() })
.then({ url: base_url + '/iguana/Proxy.UserActivities.cls',
method: 'POST',
content: this.soapEnvelope('<CurrentLoans>'
+ '<SessionId>12345678</SessionId>'
+ '<Data><From>1</From><To>20</To></Data>'
+ '</CurrentLoans>'),
headers: {"Content-Type": "text/xml; charset=UTF-8"}
},
{ content: this.loansResponse()})
.then({ url: base_url + '/iguana/Proxy.UserActivities.cls',
method: 'POST',
content: this.soapEnvelope('<CurrentReservations>'
+ '<SessionId>12345678</SessionId>'
+ '<Data><From>1</From><To>20</To></Data>'
+ '</CurrentReservations>'),
headers: {"Content-Type": "text/xml; charset=UTF-8"}
},
{ content: this.holdsResponse()})
}
public checkCredentialsResponse(): string {
return this.soapEnvelope('<CheckCredentialsResponse xmlns="http://tempuri.org">'
+ '<CheckCredentialsResult>'
+ '<Result>1</Result>'
+ '<UserName>POLO Marco</UserName>'
+ '<Avatar/>'
+ '<SearchFilter/>'
+ '<SessionId>12345678</SessionId>'
+ '<Token>TK123</Token>'
+ '</CheckCredentialsResult>'
+ '</CheckCredentialsResponse>');
}
public homePage(): string{
return '<html>\
<head>\
<script>\
Vfocus.Settings.sessionID = "2pu4GQqnf0";\
</head>\
<body></body>\
</html>';
}
public loansResponse(): string {
return this.soapEnvelope('\
<CurrentLoansResponse xmlns="http://tempuri.org">\
<CurrentLoansResult>\
<Items>\
<Item>\
<Image>http://images.fr/legumes</Image>\
<Link>1.777167</Link>\
<Barcode>32273008828837</Barcode>\
<Title>Cultiver ses légumes / Guy Barter ; traduit de l&apos;anglais par Virginie de Bermond-Gettle</Title>\
<LoanLocation>NANCY MÉDIATHÈQUE MANUFACTURE^BMN/NMANU</LoanLocation>\
<DueDate>20180719</DueDate>\
<DocType>Livre</DocType>\
<RenewalCounter>0</RenewalCounter>\
<Renewal>1</Renewal>\
<RenewalException>Dernière fois</RenewalException>\
</Item>\
<Item>\
<Image>http://images.fr/culture.jpg</Image>\
<Link>1.675461</Link>\
<Barcode>32273008675501</Barcode>\
<Title>Mes terrariums déco : cultures en bocaux, éco-systèmes autonomes et faciles à entretenir / Flore Palix</Title>\
<LoanLocation>NANCY MÉDIATHÈQUE MANUFACTURE^BMN/NMANU</LoanLocation>\
<DueDate>20180731</DueDate>\
<DocType>Livre</DocType>\
<RenewalCounter>0</RenewalCounter>\
<Renewal>1</Renewal>\
<RenewalException>Dernière fois</RenewalException>\
</Item>\
</Items>\
<RenewalAllowed>1</RenewalAllowed>\
<RenewalAdmCost>0</RenewalAdmCost>\
<TotalRecords>2</TotalRecords>\
<Sort>\
<SortBy>Title,LoanLocation,DueDate,DocType</SortBy>\
<SortCurrent>ATitle</SortCurrent>\
</Sort>\
</CurrentLoansResult>\
</CurrentLoansResponse>')
}
public frontPageConnected(): string {
return this._encapsulate('');
public holdsResponse(): string {
return this.soapEnvelope('\
<CurrentReservationsResponse xmlns="http://tempuri.org">\
<CurrentReservationsResult>\
<Items>\
<Item>\
<ReservationNumber>1</ReservationNumber>\
<Image>http://images/lumiere.jpg</Image>\
<Link>1.797549</Link>\
<Title>A la lumière du petit matin : roman / Agnès Martin-Lugand </Title>\
<DocType>Livre</DocType>\
<AvailableSince>20180723</AvailableSince>\
<PickupLocations>\
<Location>\
<Code>BMSM/BMSMA</Code>\
<Name>SAINT-MAX MÉDIATHÈQUE SECTION ADULTE</Name>\
<Selected>1</Selected>\
</Location>\
</PickupLocations>\
</Item>\
<Item>\
<ReservationNumber>5</ReservationNumber>\
<Image>http://images/paris.jpg</Image>\
<Link>1.779424</Link>\
<Title>Un appartement à Paris : roman / Guillaume Musso </Title>\
<DocType>Livre</DocType>\
<AvailableSince/>\
<PickupLocations>\
<Location>\
<Code>BMN/NMANU</Code>\
<Name>Nancy médiathèque Manufacture</Name>\
<Selected>1</Selected>\
</Location>\
</PickupLocations>\
</Item>\
</Items>\
<TotalRecords>2</TotalRecords>\
<Sort>\
<SortBy>Title,DocType</SortBy>\
<SortCurrent>ATitle</SortCurrent>\
</Sort>\
</CurrentReservationsResult>\
</CurrentReservationsResponse>');
}
}
declare var describe, expect, it, before, beforeEach, chai: any;
declare var describe, expect, it, before, beforeEach, chai: any
import {Loan, Hold, Account, Database, WrongLoginPassword, Portal} from '../../models/';
import {IguanaV3} from '../../models/portal/iguana-v3';
import {IguanaV3LoansAndHoldsWithSuccessfulLogin} from './iguana-v3-fixtures';
import {Loan, Hold, Account, Database, WrongLoginPassword, Portal} from '../../models/'
import {IguanaV3} from '../../models/portal/iguana-v3'
import {IguanaV3LoansAndHoldsWithSuccessfulLogin} from './iguana-v3-fixtures'
describe('Account on IguanaV3', () => {
let adapter: IguanaV3;
let db: Database;
let db: Database
beforeEach( () => {
db = Database.open('testdb').clear();
adapter = new IguanaV3();
});
db = Database.open('testdb').clear()
})
describe('With successful Login scenario', () => {
let account;
let account
beforeEach( () => {
account = (new Account())
.setUrl('iguana.fr')
.setCredentials({login: 'marco',
password: 'polo',
portal: 'iguana-v3'});
password: 'secret',
portal: 'iguana-v3'})
db.save(account);
});
db.save(account)
})
it('db should have three loans and one hold', () => {
it('db should have two loans and two holds', () => {
let http = new IguanaV3LoansAndHoldsWithSuccessfulLogin('http://iguana.fr')
return new IguanaV3().setHTTP(http).refresh(account).then(() => {
let loans = account.findLoans(db)
expect(loans).to.have.lengthOf(2)
expect(loans[0].getLoanId()).to.equals('32273008828837')
expect(loans[0].getTitle()).to.equals('Cultiver ses légumes')
expect(loans[0].getAuthor()).to.equals('Guy Barter')
expect(loans[0].getDateDue()).to.equals('2018-07-19')
expect(loans[0].getLibrary()).to.equals('NANCY MÉDIATHÈQUE MANUFACTURE')
expect(loans[0].getRecordId()).to.equals('1.777167')
expect(loans[0].getRecordThumbnail()).to.equals('http://images.fr/legumes')
let holds = account.findHolds(db)
expect(holds).to.have.lengthOf(2)
expect(holds[0].getHoldId()).to.equals('1')
expect(holds[0].getTitle()).to.equals('A la lumière du petit matin')
expect(holds[0].getAuthor()).to.equals('Agnès Martin-Lugand')
expect(holds[0].getStatus()).to.equals(L('hold_available'))
expect(holds[0].getLibrary()).to.equals('SAINT-MAX MÉDIATHÈQUE SECTION ADULTE')
expect(holds[0].getRecordId()).to.equals('1.797549')
expect(holds[0].getRecordThumbnail()).to.equals('http://images/lumiere.jpg')
expect(holds[1].getStatus()).to.equals(L('hold_not_available'))
})
})
})
})
......@@ -80,20 +80,11 @@ export class KohaSignInCASUL2 extends KohaCASScenario {
{
content: 'username=marco&password=polo&execution=e1s1&_eventId=submit&lt=LT-728899-oxKAugIlDkh6gp6wclE9ixoD3zA7E3&submit=CONNEXION',
dontFollowRedirects: true,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
method: 'POST',
url: 'https://cas.ul.fr/cas/login?service=https%3A%2F%2Fcas.ul.fr%2Fcgi-bin%2Fkoha%2Fopac-user.pl'
},
{
headers: { Location: 'https://ul.fr/cgi-bin/koha/opac-user.pl?ticket=ST-686-KzTBzmuP156d0m65yHR8tYmqso8-cas.ul.fr' },
statusCode: 302
})
.expect(
{ url: 'https://ul.fr/cgi-bin/koha/opac-user.pl?ticket=ST-686-KzTBzmuP156d0m65yHR8tYmqso8-cas.ul.fr' },
{ content: this.accountPage() }
)
{ content: this.accountPage() } )
.expect(
{ url: 'http://ul.fr/cgi-bin/koha/opac-memberentry.pl' },
......@@ -124,18 +115,10 @@ export class KohaSignInCASUL3 extends KohaCASScenario {
.expect(
{
content: 'username=marco&password=polo&execution=e1s1&_eventId=submit&lt=&submit=SE%20CONNECTER',
dontFollowRedirects: true,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
method: 'POST',
url: 'https://cas.ul.fr/cas/login?service=https%3A%2F%2Fcas.ul.fr%2Fcgi-bin%2Fkoha%2Fopac-user.pl'
},
{
headers: { Location: 'https://ul.fr/cgi-bin/koha/opac-user.pl?ticket=ST-686-KzTBzmuP156d0m65yHR8tYmqso8-cas.ul.fr' },
statusCode: 302
})
.expect(
{ url: 'https://ul.fr/cgi-bin/koha/opac-user.pl?ticket=ST-686-KzTBzmuP156d0m65yHR8tYmqso8-cas.ul.fr' },
{ content: this.preAccountPage() } )
.expect(
......
......@@ -54,7 +54,6 @@ export class OrpheeMediaWithCasLoansSuccessful extends OrpheeMediaHTTPScenario {
.then({ url: base_url,
method: "POST",
dontFollowRedirects: true,
content: "carte=marco&pwd=polo&option=com_opac&task=login&mod_id=112&Itemid=0&submit=Ok&ajs=&return=aHR0cDovL2JpYmxpb3RoZXF1ZXMuYWdnbG8tYW5uZWN5LmZyLw%3D%3D&970fcab6cdadc6e526687e845eeb098a=1",
headers: {"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": this._user_agent} },
......@@ -172,7 +171,6 @@ abstract class OrpheeMediaWithoutCas extends OrpheeMediaHTTPScenario {
.then({ url: base_url,
method: "POST",
dontFollowRedirects: true,
content: "nom=chris&pwd=colomb&option=com_opac&task=login&mod_id=71&Itemid=0&submit=Ok&ajs=&return=aW5kZXgucGhwP0l0ZW1pZD0yNDk%3D&ec01a2d803c4bf052dc92efe54cdb625=1",
headers: {"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": this._user_agent} },
......@@ -250,7 +248,6 @@ export class OrpheeMediaWithTwoPagesCasSignInSuccessful extends OrpheeMediaHTTPS
.then({ url: 'http://bibliofil.fr',
method: "POST",
dontFollowRedirects: true,
content: "carte=marco&birth_day=2&birth_month=12&birth_year=1987&option=com_opac&task=login&mod_id=112&Itemid=0&submit=Ok&ajs=&return=aHR0cDovL2JpYmxpb3RoZXF1ZXMuYWdnbG8tYW5uZWN5LmZyLw%3D%3D&970fcab6cdadc6e526687e845eeb098a=1",
headers: {"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": this._user_agent} },
......
......@@ -15,6 +15,7 @@
"dependencies": {
"cheerio": "^0.22.0",
"events": "^1.1.1",
"jstoxml": "^1.4.3",
"mocha": "^3.4.2",
"nativescript-barcodescanner": "^2.7.7",
"nativescript-couchbase": "git+https://github.com/Afibre/nativescript-couchbase.git",
......@@ -31,7 +32,8 @@
"tns-core-modules": "^4.1.0",
"tns-platform-declarations": "^3.4.0",
"url-parse": "^1.2.0",
"validator": "^7.0.0"
"validator": "^7.0.0",
"xml-js": "^1.6.7"
},
"devDependencies": {
"awesome-typescript-loader": "~3.1.3",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment