-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
SPSH-624 Implemented itslearning client (#535)
* Implemented itslearning client
- Loading branch information
1 parent
51ae7bd
commit 1859fbe
Showing
27 changed files
with
782 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { DomainError } from '../../../shared/error/domain.error.js'; | ||
import { ItsLearningError } from '../../../shared/error/its-learning.error.js'; | ||
import { IMSESAction } from './base-action.js'; | ||
import { faker } from '@faker-js/faker'; | ||
|
||
function buildXMLResponse(codeMajor: 'success' | 'failure', severity: 'status' | 'error', body: string): string { | ||
return `<s:Envelope | ||
xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" | ||
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> | ||
<s:Header> | ||
<h:syncResponseHeaderInfo | ||
xmlns:h="http://www.imsglobal.org/services/common/imsMessBindSchema_v1p0" | ||
xmlns="http://www.imsglobal.org/services/common/imsMessBindSchema_v1p0" | ||
xmlns:xsd="http://www.w3.org/2001/XMLSchema" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> | ||
<messageIdentifier/> | ||
<statusInfo> | ||
<codeMajor>${codeMajor}</codeMajor> | ||
<severity>${severity}c</severity> | ||
<messageIdRef/> | ||
</statusInfo> | ||
</h:syncResponseHeaderInfo> | ||
<o:Security s:mustUnderstand="1" | ||
xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> | ||
<u:Timestamp u:Id="_0"> | ||
<u:Created>${faker.date.recent().toISOString()}</u:Created> | ||
<u:Expires>${faker.date.soon().toISOString()}</u:Expires> | ||
</u:Timestamp> | ||
</o:Security> | ||
</s:Header> | ||
<s:Body | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"> | ||
${body} | ||
</s:Body> | ||
</s:Envelope>`; | ||
} | ||
|
||
type DummyResponse = { | ||
dummyResponse: string; | ||
}; | ||
|
||
class TestAction extends IMSESAction<DummyResponse, string> { | ||
public action: string = faker.internet.url(); | ||
|
||
public buildRequest(): object { | ||
return {}; | ||
} | ||
|
||
public parseBody(body: DummyResponse): Result<string, DomainError> { | ||
return { | ||
ok: true, | ||
value: body.dummyResponse, | ||
}; | ||
} | ||
} | ||
|
||
describe('IMSESAction', () => { | ||
describe('parseResponse', () => { | ||
it('should parse XML', () => { | ||
const xmlTest: string = buildXMLResponse('success', 'status', '<dummyResponse>test</dummyResponse>'); | ||
const testAction: TestAction = new TestAction(); | ||
|
||
const result: Result<string, DomainError> = testAction.parseResponse(xmlTest); | ||
|
||
expect(result).toEqual({ | ||
ok: true, | ||
value: 'test', | ||
}); | ||
}); | ||
|
||
it('should return ItsLearningError if response is an error', () => { | ||
const xmlTest: string = buildXMLResponse('failure', 'error', '<dummyResponse/>'); | ||
const testAction: TestAction = new TestAction(); | ||
|
||
const result: Result<string, DomainError> = testAction.parseResponse(xmlTest); | ||
|
||
expect(result).toEqual({ | ||
ok: false, | ||
error: new ItsLearningError('Request failed', expect.anything() as Record<string, unknown>), | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import { XMLBuilder, XMLParser } from 'fast-xml-parser'; | ||
|
||
import { DomainError, ItsLearningError } from '../../../shared/error/index.js'; | ||
|
||
export type StatusInfo = | ||
| { | ||
codeMajor: 'failure'; | ||
severity: 'error'; | ||
} | ||
| { | ||
codeMajor: 'success'; | ||
severity: 'status'; | ||
}; | ||
|
||
export type BaseResponse<BodyResponse> = { | ||
Envelope: { | ||
Header: { | ||
syncResponseHeaderInfo: { | ||
statusInfo: StatusInfo; | ||
}; | ||
}; | ||
|
||
Body: BodyResponse; | ||
}; | ||
}; | ||
|
||
export abstract class IMSESAction<ResponseBodyType, ResultType> { | ||
protected readonly xmlBuilder: XMLBuilder = new XMLBuilder({ ignoreAttributes: false }); | ||
|
||
protected readonly xmlParser: XMLParser = new XMLParser({ | ||
ignoreAttributes: false, | ||
removeNSPrefix: true, | ||
isArray: (tagName: string, jPath: string, isLeafNode: boolean, isAttribute: boolean) => | ||
this.isArrayOverride(tagName, jPath, isLeafNode, isAttribute), | ||
}); | ||
|
||
public abstract action: string; | ||
|
||
public abstract buildRequest(): object; | ||
|
||
// Customize parsing behaviour, see X2jOptions.isArray | ||
public isArrayOverride(_tagName: string, _jPath: string, _isLeafNode: boolean, _isAttribute: boolean): boolean { | ||
return false; | ||
} | ||
|
||
/** | ||
* Will be called if the response was successful | ||
* @param body The contents of the response body | ||
*/ | ||
public abstract parseBody(body: ResponseBodyType): Result<ResultType, DomainError>; | ||
|
||
public parseResponse(input: string): Result<ResultType, DomainError> { | ||
const result: BaseResponse<ResponseBodyType> = this.xmlParser.parse(input) as BaseResponse<ResponseBodyType>; | ||
|
||
if (result.Envelope.Header.syncResponseHeaderInfo.statusInfo.codeMajor === 'failure') { | ||
return { | ||
ok: false, | ||
error: new ItsLearningError('Request failed', result), | ||
}; | ||
} else { | ||
return this.parseBody(result.Envelope.Body); | ||
} | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
src/modules/itslearning/actions/create-group.action.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { faker } from '@faker-js/faker'; | ||
import { CreateGroupAction } from './create-group.action.js'; | ||
|
||
describe('CreateGroupAction', () => { | ||
describe('buildRequest', () => { | ||
it('should return object', () => { | ||
const action: CreateGroupAction = new CreateGroupAction({ | ||
id: faker.string.uuid(), | ||
name: `${faker.word.adjective()} school`, | ||
parentId: faker.string.uuid(), | ||
type: 'School', | ||
}); | ||
|
||
expect(action.buildRequest()).toBeDefined(); | ||
}); | ||
}); | ||
|
||
describe('parseBody', () => { | ||
it('should void result', () => { | ||
const action: CreateGroupAction = new CreateGroupAction({ | ||
id: faker.string.uuid(), | ||
name: `${faker.word.adjective()} school`, | ||
parentId: faker.string.uuid(), | ||
type: 'School', | ||
}); | ||
|
||
expect(action.parseBody()).toEqual({ | ||
ok: true, | ||
value: undefined, | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { DomainError } from '../../../shared/error/domain.error.js'; | ||
import { IMS_COMMON_SCHEMA, IMS_GROUP_MAN_DATA_SCHEMA, IMS_GROUP_MAN_MESS_SCHEMA } from '../schemas.js'; | ||
import { IMSESAction } from './base-action.js'; | ||
|
||
// Incomplete | ||
export type CreateGroupParams = { | ||
id: string; | ||
|
||
name: string; | ||
type: 'School' | 'Course' | 'CourseGroup'; | ||
|
||
parentId: string; | ||
relationLabel?: string; | ||
|
||
longDescription?: string; | ||
fullDescription?: string; | ||
}; | ||
|
||
type CreateGroupResponseBody = { | ||
createGroupResponse: undefined; | ||
}; | ||
|
||
export class CreateGroupAction extends IMSESAction<CreateGroupResponseBody, void> { | ||
public override action: string = 'http://www.imsglobal.org/soap/gms/createGroup'; | ||
|
||
public constructor(private readonly params: CreateGroupParams) { | ||
super(); | ||
} | ||
|
||
public override buildRequest(): object { | ||
return { | ||
'ims:createGroupRequest': { | ||
'@_xmlns:ims': IMS_GROUP_MAN_MESS_SCHEMA, | ||
'@_xmlns:ims1': IMS_COMMON_SCHEMA, | ||
'@_xmlns:ims2': IMS_GROUP_MAN_DATA_SCHEMA, | ||
|
||
'ims:sourcedId': { | ||
'ims1:identifier': this.params.id, | ||
}, | ||
|
||
'ims:group': { | ||
'ims2:groupType': { | ||
'ims2:scheme': 'ItslearningOrganisationTypes', | ||
'ims2:typeValue': { | ||
'ims2:type': this.params.type, | ||
}, | ||
}, | ||
'ims2:relationship': { | ||
'ims2:relation': 'Parent', | ||
'ims2:sourceId': { | ||
'ims1:identifier': this.params.parentId, | ||
}, | ||
'ims2:label': this.params.relationLabel, | ||
}, | ||
'ims2:description': { | ||
'ims2:descShort': this.params.name, | ||
'ims2:descLong': this.params.longDescription, | ||
'ims2:descFull': this.params.fullDescription, | ||
}, | ||
}, | ||
}, | ||
}; | ||
} | ||
|
||
public override parseBody(): Result<void, DomainError> { | ||
// Response does not contain data | ||
return { | ||
ok: true, | ||
value: undefined, | ||
}; | ||
} | ||
} |
Oops, something went wrong.