├── test
├── .gitignore
├── replies
│ ├── soap
│ │ ├── AddBilledJob.ok.xml
│ │ ├── AddRecruiterV2.ok.xml
│ │ ├── UpdateBilledJob.ok.xml
│ │ ├── UpdateRecruiterWithBillingID.ok.xml
│ │ ├── GetCategoryTerms.single.xml
│ │ ├── CheckRecruiterExistsV2.single.xml
│ │ ├── GetCategories.single.xml
│ │ ├── GetLocations.single.xml
│ │ ├── GetCategories.many.xml
│ │ ├── SoapFault.xml
│ │ ├── GetCategoryTerms.many.xml
│ │ └── GetLocations.many.xml
│ └── rest
│ │ ├── get.jobinfo.jobid.json
│ │ ├── get.jobinfo.search.facets.json
│ │ ├── get.employer.search.json
│ │ └── get.jobinfo.search.json
├── rest-api-tests.js
└── soap-api-tests.js
├── .travis.yml
├── lib
├── parseBooleans.js
├── soap-templates
│ ├── GetCategories.hbs
│ ├── GetLocations.hbs
│ ├── GetCategoryTerms.hbs
│ ├── CheckRecruiterExistsV2.hbs
│ ├── UpdateBilledJob.hbs
│ ├── AddBilledJob.hbs
│ ├── AddRecruiterV2.hbs
│ └── UpdateRecruiterWithBillingID.hbs
├── util.js
├── soap-api-service-description.json
├── rest-api-service-description.json
├── rest-api-client.js
└── soap-api-client.js
├── index.js
├── .gitignore
├── LICENSE
├── package.json
└── README.md
/test/.gitignore:
--------------------------------------------------------------------------------
1 | service-config.json
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "0.10"
4 |
--------------------------------------------------------------------------------
/lib/parseBooleans.js:
--------------------------------------------------------------------------------
1 | // https://github.com/Leonidas-from-XIV/node-xml2js/pull/200
2 |
3 | module.exports = function(str) {
4 | return /^(?:true|false)$/i.test(str) ? str.toLowerCase() === 'true' : str
5 | }
--------------------------------------------------------------------------------
/lib/soap-templates/GetCategories.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/lib/soap-templates/GetLocations.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{prefix}}
6 |
7 |
8 |
--------------------------------------------------------------------------------
/lib/soap-templates/GetCategoryTerms.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{categoryId}}
6 |
7 |
8 |
--------------------------------------------------------------------------------
/test/replies/soap/AddBilledJob.ok.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1340
5 |
6 |
7 |
--------------------------------------------------------------------------------
/test/replies/soap/AddRecruiterV2.ok.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1339
5 |
6 |
7 |
--------------------------------------------------------------------------------
/test/replies/soap/UpdateBilledJob.ok.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1340
5 |
6 |
7 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var debug = require('debug')('node-madgex')
2 | var utils = require('./lib/util.js')
3 | var restClient = require('./lib/rest-api-client.js')
4 | var soapClient = require('./lib/soap-api-client.js')
5 |
6 | module.exports = {
7 | utils: utils,
8 | createClient: function(baseUrl, credentials) {
9 | return {
10 | restApi: restClient(baseUrl, credentials),
11 | soapApi: soapClient(baseUrl)
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/test/replies/soap/UpdateRecruiterWithBillingID.ok.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1339
5 |
6 |
7 |
--------------------------------------------------------------------------------
/lib/soap-templates/CheckRecruiterExistsV2.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{recruiterName}}
6 | {{customerBillingId}}
7 | {{madgexID}}
8 |
9 |
10 |
--------------------------------------------------------------------------------
/lib/util.js:
--------------------------------------------------------------------------------
1 |
2 | var defaultEnvironment = 'development',
3 | serviceUrlSuffix = {
4 | 'development': '-webservice.madgexjbtest.com',
5 | 'production': '-webservice.madgexjb.com',
6 | getCurrent: function () {
7 | return serviceUrlSuffix[process.env.NODE_ENV || defaultEnvironment ]
8 | }
9 | }
10 |
11 |
12 |
13 | module.exports = {
14 | serviceUrlSuffix : serviceUrlSuffix,
15 | getHostName: function (sitename) {
16 | return sitename + module.exports.serviceUrlSuffix.getCurrent();
17 | }
18 | }
--------------------------------------------------------------------------------
/test/replies/soap/GetCategoryTerms.single.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 21
7 | Sport and Leisure
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/test/replies/soap/CheckRecruiterExistsV2.single.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | true
6 | 11713
7 | 23005869
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/test/replies/soap/GetCategories.single.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | false
7 | true
8 | 105
9 | Hours
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/test/replies/soap/GetLocations.single.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 17116
7 |
8 | Longney
9 | GB
10 | 10
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # node-waf configuration
20 | .lock-wscript
21 |
22 | # Compiled binary addons (http://nodejs.org/api/addons.html)
23 | build/Release
24 |
25 | # Dependency directory
26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
27 | node_modules
28 |
29 | test/service-config.json
30 |
31 | .ntvs_analysis.dat
32 |
33 | #Visual Studio stuff
34 |
35 | *.sln
36 |
37 | *.suo
38 |
39 | *.njsproj
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 GuideSmiths Ltd
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "node-madgex",
3 | "version": "0.0.6",
4 | "description": "node-madgex",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "mocha --recursive test",
8 | "test-debug": "mocha --debug-brk --recursive test",
9 | "prepush": "npm test"
10 | },
11 | "author": "GuideSmiths ",
12 | "dependencies": {
13 | "change-case": "^2.3.0",
14 | "cheerio": "^0.19.0",
15 | "debug": "^2.1.3",
16 | "extend": "^2.0.0",
17 | "handlebars": "^3.0.3",
18 | "json-pointer": "^0.3.0",
19 | "lodash": "^3.8.0",
20 | "nock": "^1.3.0",
21 | "oauth": "^0.9.12",
22 | "request": "^2.55.0",
23 | "when": "^3.7.2",
24 | "xml2js": "^0.4.8"
25 | },
26 | "devDependencies": {
27 | "husky": "^0.7.0",
28 | "mocha": "^2.2.4"
29 | },
30 | "directories": {
31 | "test": "test"
32 | },
33 | "repository": {
34 | "type": "git",
35 | "url": "https://github.com/guidesmiths/node-madgex.git"
36 | },
37 | "keywords": [
38 | "madgex",
39 | "client",
40 | "SOAP",
41 | "REST"
42 | ],
43 | "license": "ISC",
44 | "bugs": {
45 | "url": "https://github.com/guidesmiths/node-madgex/issues"
46 | },
47 | "homepage": "https://github.com/guidesmiths/node-madgex"
48 | }
49 |
--------------------------------------------------------------------------------
/lib/soap-api-service-description.json:
--------------------------------------------------------------------------------
1 | {
2 | "billingApi": {
3 | "path": "/billing.asmx",
4 | "services": {
5 | "GetCategories": {
6 | "forceArray": true,
7 | "result": "/soap:Envelope/soap:Body/getCategoriesResponse/getCategoriesResult/wSCategory"
8 | },
9 | "GetCategoryTerms": {
10 | "forceArray": true,
11 | "result": "/soap:Envelope/soap:Body/getCategoryTermsResponse/getCategoryTermsResult/wSTerm"
12 | },
13 | "GetLocations": {
14 | "forceArray": true,
15 | "result": "/soap:Envelope/soap:Body/getLocationsResponse/getLocationsResult/wSLocation"
16 | },
17 | "AddBilledJob": {
18 | "result": "/soap:Envelope/soap:Body/addBilledJobResponse/addBilledJobResult"
19 | },
20 | "UpdateBilledJob": {
21 | "result": "/soap:Envelope/soap:Body/updateBilledJobResponse/updateBilledJobResult"
22 | },
23 | "AddRecruiterV2": {
24 | "result": "/soap:Envelope/soap:Body/addRecruiterV2Response/addRecruiterV2Result"
25 | },
26 | "UpdateRecruiterWithBillingID": {
27 | "result": "/soap:Envelope/soap:Body/updateRecruiterWithBillingIDResponse/updateRecruiterWithBillingIDResult"
28 | },
29 | "CheckRecruiterExistsV2": {
30 | "result": "/soap:Envelope/soap:Body/checkRecruiterExistsV2Response/checkRecruiterExistsV2Result"
31 | }
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/test/replies/soap/GetCategories.many.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | false
8 | true
9 | 100
10 | Academic Discipline
11 |
12 |
13 | true
14 | true
15 | 101
16 | Location
17 |
18 |
19 | false
20 | true
21 | 102
22 | Job Type
23 |
24 |
25 | false
26 | true
27 | 103
28 | Salary Band
29 |
30 |
31 | false
32 | true
33 | 104
34 | Contract Type
35 |
36 |
37 | false
38 | true
39 | 105
40 | Hours
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/lib/rest-api-service-description.json:
--------------------------------------------------------------------------------
1 | {
2 | "invokable": false,
3 | "uriTemplate": "restapi",
4 | "sub": {
5 | "jobinfo": {
6 | "uriTemplate": "jobinfo",
7 | "invokable": true,
8 | "params": {
9 | "jobid": { "type": "integer", "required": true }
10 | },
11 | "sub": {
12 | "search": {
13 | "uriTemplate": "search",
14 | "invokable": true,
15 | "params": { },
16 | "sub": {
17 | "full": {
18 | "invokable": true,
19 | "uriTemplate": "full"
20 | },
21 | "facets": {
22 | "invokable": true,
23 | "uriTemplate": "facets"
24 | }
25 | }
26 | }
27 | }
28 | },
29 | "employer": {
30 | "invokable": true,
31 | "params": {
32 | "id": { "type": "integer" }
33 | },
34 | "uriTemplate": "employer",
35 | "sub": {
36 | "search": {
37 | "invokable": true,
38 | "uriTemplate": "search"
39 | }
40 | }
41 | },
42 | "myjobs": {
43 | "uriTemplate": "myjobs",
44 | "invokable": true,
45 | "sub": {
46 | "add": {
47 | "invokable": true,
48 | "uriTemplate": "add",
49 | "params": {
50 | "jobid": { "type": "integer" }
51 | }
52 | },
53 | "delete": {
54 | "invokable": true,
55 | "uriTemplate": "list",
56 | "params": {
57 | "jobid": { "type": "integer" }
58 | }
59 | }
60 | }
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/lib/soap-templates/UpdateBilledJob.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{recruiterBillingId}}
6 | {{employerBillingId}}
7 | {{id}}
8 | {{#if startDateTime}}{{startDateTime}}{{/if}}
9 | {{#if endDateTime}}{{endDateTime}}{{/if}}
10 | {{#if applicationMethod}}{{applicationMethod}}{{/if}}
11 | {{#if applicationEmail}}{{applicationEmail}}{{/if}}
12 | {{#if externalApplicationUrl}}{{externalApplicationUrl}}{{/if}}
13 |
14 | {{#each properties}}
15 |
16 | {{@key}}
17 | {{this}}
18 |
19 | {{/each}}
20 |
21 |
22 | {{#each categorization.terms}}
23 |
24 | {{categoryId}}
25 | {{#if termIds}}
26 |
27 | {{#each termIds}}
28 | {{this}}
29 | {{/each}}
30 |
31 | {{/if}}
32 |
33 | {{/each}}
34 |
35 | {{#defined isBackFill}}{{isBackFill}}{{else}}{{/defined}}
36 |
37 |
38 |
--------------------------------------------------------------------------------
/test/replies/soap/SoapFault.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | soap:Server
5 | System.Web.Services.Protocols.SoapException: Server was unable to process request. ---> JobBoard.WebService.JobBoardException: An unexpected error occurred. ---> JobBoard.WebService.JobBoardException: The mandatory category:Location - ID:101 has no terms supplied
6 | at JobBoard.WebService.JobPostingImpl.ValidateCategories(WSSelectedTerms[] jobCategorization, ResolvedLocationIds resolvedLocationIds)
7 | at JobBoard.WebService.JobPostingImpl.AssignJobProperties(Guid UserID, Guid uiSecurityToken, ActionType actionType, Job job, String sJobID, String sStartDateTime, String sEndDateTime, String sApplicationMethod, String sApplicationEmail, String sExternalApplicationURL, WSJobPropertyValue[] jobProperties, WSSelectedTerms[] jobCategorization, DateTime maxAllowedEndDate, Int32 productDefinitionId, Nullable`1 isBackFill, Nullable`1 priceSoldAt)
8 | at JobBoard.WebService.JobPostingImpl.UpdateBilledJob(String sRecruiterBillingID, String sEmployerBillingID, String sJobID, String sEndDateTime, String sApplicationMethod, String sApplicationEmail, String sExternalApplicationURL, WSJobPropertyValue[] jobProperties, WSSelectedTerms[] jobCategorization, Nullable`1 isBackFill)
9 | at JobBoard.WebService.Billing.UpdateBilledJob(String sRecruiterBillingID, String sEmployerBillingID, String sJobID, String sEndDateTime, String sApplicationMethod, String sApplicationEmail, String sExternalApplicationURL, WSJobPropertyValue[] jobProperties, WSSelectedTerms[] jobCategorization, Nullable`1 isBackFill)
10 | --- End of inner exception stack trace ---
11 | at JobBoard.WebService.Billing.UpdateBilledJob(String sRecruiterBillingID, String sEmployerBillingID, String sJobID, String sEndDateTime, String sApplicationMethod, String sApplicationEmail, String sExternalApplicationURL, WSJobPropertyValue[] jobProperties, WSSelectedTerms[] jobCategorization, Nullable`1 isBackFill)
12 | --- End of inner exception stack trace ---
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/lib/soap-templates/AddBilledJob.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{recruiterBillingId}}
6 | {{employerBillingId}}
7 | {{id}}
8 | {{#if startDateTime}}{{startDateTime}}{{/if}}
9 | {{#if endDateTime}}{{endDateTime}}{{/if}}
10 | {{#if applicationMethod}}{{applicationMethod}}{{/if}}
11 | {{#if applicationEmail}}{{applicationEmail}}{{/if}}
12 | {{#if externalApplicationUrl}}{{externalApplicationUrl}}{{/if}}
13 |
14 | {{#each properties}}
15 |
16 | {{@key}}
17 | {{this}}
18 |
19 | {{/each}}
20 |
21 |
22 | {{#each categorization.terms}}
23 |
24 | {{categoryId}}
25 | {{#if termIds}}
26 |
27 | {{#each termIds}}
28 | {{this}}
29 | {{/each}}
30 |
31 | {{/if}}
32 |
33 | {{/each}}
34 |
35 | {{#if upsells}}
36 |
37 | {{#each upsells}}
38 |
39 | {{this.id}}
40 | {{#if this.name}}{{this.name}}{{/if}}
41 |
42 | {{/each}}
43 |
44 | {{/if}}
45 | {{#defined productId}}{{productId}}{{else}}{{/defined}}
46 | {{#defined priceSoldAt}}{{priceSoldAt}}{{else}}{{/defined}}
47 | {{#defined isBackFill}}{{isBackFill}}{{else}}{{/defined}}
48 |
49 |
50 |
--------------------------------------------------------------------------------
/test/replies/soap/GetCategoryTerms.many.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 1
7 | Agriculture, Food and Veterinary
8 |
9 |
10 | 2
11 | Architecture, Building and Planning
12 |
13 |
14 | 3
15 | Biological Sciences
16 |
17 |
18 | 4
19 | Business and Management Studies
20 |
21 |
22 | 5
23 | Computer Science
24 |
25 |
26 | 6
27 | Creative Arts and Design
28 |
29 |
30 | 7
31 | Economics
32 |
33 |
34 | 8
35 | Education Studies
36 |
37 |
38 | 9
39 | Engineering and Technology
40 |
41 |
42 | 10
43 | Health and Medical
44 |
45 |
46 | 11
47 | Historical and Philosophical Studies
48 |
49 |
50 | 12
51 | Information Management and Librarianship
52 |
53 |
54 | 13
55 | Languages, Literature and Culture
56 |
57 |
58 | 14
59 | Law
60 |
61 |
62 | 15
63 | Mathematics and Statistics
64 |
65 |
66 | 16
67 | Media and Communications
68 |
69 |
70 | 17
71 | Physical and Environmental Sciences
72 |
73 |
74 | 18
75 | Politics and Government
76 |
77 |
78 | 19
79 | Psychology
80 |
81 |
82 | 20
83 | Social Sciences and Social Care
84 |
85 |
86 | 21
87 | Sport and Leisure
88 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/lib/rest-api-client.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var oauth = require('oauth'),
4 | debug = require('debug')('node-madgex'),
5 | extend = require('extend'),
6 | qs = require('querystring'),
7 | when = require('when'),
8 | getUrlFactory = function (baseUrl, requestOptions) {
9 | return function (operation, params, pathOnly) {
10 | params = extend({}, params, requestOptions || { deviceId: 'node-device' });
11 | var result = operation + "?" + qs.stringify(params);
12 | if (!pathOnly) {
13 | result = baseUrl + result;
14 | }
15 | debug('url: %s', result);
16 | return result;
17 | };
18 | };
19 |
20 | function getDefaulOptions() {
21 | //deviceId is a mandatory request parameter for the Madgex REST api user can set this during the createClient call
22 | return {
23 | requestOptions: { deviceId: 'node-device' }
24 | };
25 | }
26 |
27 | var serviceDefinition = require('./rest-api-service-description.json');
28 |
29 | module.exports = function (baseUrl, credentials, options) {
30 |
31 | var request = new oauth.OAuth(null, null, credentials.key, credentials.secret, '1.0', null, 'HMAC-SHA1'),
32 | options = options || getDefaulOptions(),
33 | urlFactory = getUrlFactory(baseUrl, options.requestOptions);
34 |
35 | function buildFacade(sd, parentPath) {
36 | var result = buildFacadeItem(sd, parentPath);
37 | sd.sub && Object.keys(sd.sub).forEach(function (key) {
38 | result[key] = buildFacade(sd.sub[key], parentPath + "/" + sd.uriTemplate);
39 | });
40 | return result;
41 | }
42 |
43 | function buildFacadeItem(sd, parentPath) {
44 | if (!sd.invokable) return {};
45 |
46 | var path = (parentPath + "/" + sd.uriTemplate).replace("//", "/");
47 | var result = function (params, done) {
48 | var getResult = function (handleResponse) {
49 | request.get(urlFactory(path, params), null, null, function (error, data) {
50 | if (error) return handleResponse(error);
51 | try {
52 | data = JSON.parse(data);
53 | }
54 | catch (err) {
55 | return handleResponse(err);
56 | }
57 | if (data.status !== "ok") {
58 | var message = JSON.stringify(data.result);
59 | if (Array.isArray(data.result.errors)) {
60 | var firstError = data.result.errors[0];
61 | if (firstError.message) {
62 | message = firstError.message;
63 | }
64 | }
65 | return handleResponse(new Error(message));
66 | }
67 | handleResponse(null, data.result);
68 | });
69 | };
70 |
71 | if (done && typeof done === 'function') {
72 | return getResult(done);
73 | }
74 |
75 | return when.promise(function (resolve, reject) {
76 | return getResult(function (err, data) {
77 | return (err) ? reject(err) : resolve(data);
78 | });
79 | });
80 |
81 | };
82 | result.path = path;
83 | result.getPathAndQuery = function (params) {
84 | return urlFactory(result.path, params, true);
85 | };
86 | return result;
87 | }
88 |
89 | var restApi = buildFacade(serviceDefinition, "");
90 | restApi.urlFactory = urlFactory;
91 | return restApi;
92 | };
93 |
--------------------------------------------------------------------------------
/test/replies/rest/get.jobinfo.jobid.json:
--------------------------------------------------------------------------------
1 | {
2 | "status": "ok",
3 | "result": {
4 | "id": 1257,
5 | "title": "Senior Lecturer in Criminology",
6 | "posted": "21 Apr 2015",
7 | "description": "NEWMAN UNIVERSITY\r\n\r\n\r\n\r\nSenior Lecturer in Criminology\r\n\r\n\r\n\r\nSalary scale: \u00a336,308 to \u00a347,328 per annum\r\n\r\n\r\n\r\nRequired from 1st August 2015 (or as soon as possible thereafter), an experienced senior lecturer to contribute to the leadership and further development of our new BA (Hons) Criminology degree. With a relevant master's qualification and demonstrable expertise in criminal justice and policing, the successful applicant will teach on a broad range of undergraduate modules on Criminology and related applied social science programmes. Demonstrably research active in a relevant field of criminology, you will be an effective networker with the ability to establish appropriate external links to enhance the curriculum and the student experience.\r\n\r\n\r\n\r\nThis post is offered on a full-time, permanent basis, and candidates for appointment as senior lecturer must hold or have recently submitted for a doctoral qualification in a relevant area of criminology \/ criminal justice.\r\n\r\n\r\n\r\nApplicants wishing to have an informal discussion about the post should contact Frank Leishman, Dean of School - Human Sciences on 0121 476 1181 ext 2228.\r\n\r\n\r\n\r\nFurther details of posts and an application form may be obtained from:\r\n\r\n\r\n\r\nwww.newman.ac.uk\/jobs or alternatively e-mail: recruitment@newman.ac.uk or telephone 0121 476 1181 ext 2398.\r\n\r\n\r\n\r\nClosing date for applications: Friday 20th March 2015\r\n",
8 | "recruiter": "Newman University",
9 | "recruiterID": 1255,
10 | "recruiterLogo": "6583fa39-0812-4142-b3c6-d8662b488790",
11 | "salary": "\u00a336,308 to \u00a347,328 per annum",
12 | "location": "Birmingham, West Midlands",
13 | "geo": [
14 |
15 | ],
16 | "jobRole": null,
17 | "applyInfo": {
18 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1257\/senior-lecturer-in-criminology\/?trackid=22",
19 | "type": "external-redirect",
20 | "allowMobileExternalRedirects": true,
21 | "externalApplicationUrl": "http:\/\/www.newman.ac.uk\/jobs"
22 | },
23 | "upsells": [
24 | 5,
25 | 6,
26 | 7,
27 | 8
28 | ],
29 | "noOfVideos": 0,
30 | "isPremiumJob": false,
31 | "isHomePageJob": false,
32 | "isDisplayLogoJob": true,
33 | "isTopJob": false,
34 | "categories": {
35 | "function": [
36 | [
37 | "Academic Posts",
38 | [
39 | "Lecturers \/ Assistant Professors"
40 | ]
41 | ]
42 | ],
43 | "location": [
44 |
45 | ],
46 | "discipline": [
47 | "Psychology",
48 | "Social Sciences and Social Care"
49 | ],
50 | "contract": [
51 | "Permanent"
52 | ],
53 | "salary": [
54 |
55 | ]
56 | },
57 | "urlId": "1257\/senior-lecturer-in-criminology",
58 | "usesAltRecruiterName": false,
59 | "ref": "",
60 | "status": "Live",
61 | "jobFunction": [
62 | [
63 | "Academic Posts",
64 | [
65 | "Lecturers \/ Assistant Professors"
66 | ]
67 | ]
68 | ],
69 | "jobDiscipline": [
70 | "Psychology",
71 | "Social Sciences and Social Care"
72 | ],
73 | "contractType": [
74 | "Permanent"
75 | ],
76 | "contactInfo": {
77 | "contactTitle": null,
78 | "contactFirstName": null,
79 | "contactLastName": null,
80 | "contactLastName2": null,
81 | "contactAddress1": "",
82 | "contactAddress2": "",
83 | "contactAddress3": "",
84 | "contactTown": "",
85 | "contactCounty": "",
86 | "contactPostcode": "",
87 | "contactCountry": "",
88 | "contactPhone": "",
89 | "contactFax": "",
90 | "contactEmail": null
91 | }
92 | }
93 | }
--------------------------------------------------------------------------------
/lib/soap-templates/AddRecruiterV2.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{#if recruiterName}}{{recruiterName}}{{/if}}
7 | {{#if customerBillingID}}{{customerBillingID}}{{/if}}
8 | {{#if contactFirstName}}{{contactFirstName}}{{/if}}
9 | {{#if contactLastName}}{{contactLastName}}{{/if}}
10 | {{#if contactLastName2}}{{contactLastName2}}{{/if}}
11 | {{#if address1}}{{address1}}{{/if}}
12 | {{#if address2}}{{address2}}{{/if}}
13 | {{#if address3}}{{address3}}{{/if}}
14 | {{#if townCity}}{{townCity}}{{/if}}
15 | {{#if countyState}}{{countyState}}{{/if}}
16 | {{#if zipPostCode}}{{zipPostCode}}{{/if}}
17 | {{#if country}}{{country}}{{/if}}
18 | {{#if telephone}}{{telephone}}{{/if}}
19 | {{#if contactEmail}}{{contactEmail}}{{/if}}
20 | {{recruiterTypeID}}
21 | {{#if image}}
22 |
23 | {{#if image.assetId}}{{image.assetId}}{{/if}}
24 | {{#if image.base64BlobString}}{{image.base64BlobString}}{{/if}}
25 |
26 | {{/if}}
27 | {{#if enhancedJobHtml}}
28 |
29 | {{#if enhancedJobHtml.jobTitleTextCss}}{{enhancedJobHtml.jobTitleTextCss}}{{/if}}
30 | {{#if enhancedJobHtml.jobHeadingsTextCss}}{{enhancedJobHtml.jobHeadingsTextCss}}{{/if}}
31 | {{#if enhancedJobHtml.buttonBackgroundCss}}{{enhancedJobHtml.buttonBackgroundCss}}{{/if}}
32 | {{#if enhancedJobHtml.buttonTextCss}}{{enhancedJobHtml.buttonTextCss}}{{/if}}
33 | {{#if enhancedJobHtml.hyperLinkCss}}{{enhancedJobHtml.hyperLinkCss}}{{/if}}
34 | {{#if enhancedJobHtml.banner}}
35 |
36 | {{#if enhancedJobHtml.banner.assetId}}{{enhancedJobHtml.banner.assetId}}{{/if}}
37 | {{#if enhancedJobHtml.banner.base64BlobString}}{{enhancedJobHtml.banner.base64BlobString}}{{/if}}
38 |
39 | {{/if}}
40 |
41 | {{/if}}
42 | {{#if accountManager}}
43 |
44 | {{#if accountManager.emailAddress}}{{accountManager.emailAddress}}{{/if}}
45 | {{#if accountManager.firstName}}{{accountManager.firstName}}{{/if}}
46 | {{#if accountManager.lastName}}{{accountManager.lastName}}{{/if}}
47 |
48 | {{/if}}
49 | {{#defined canAccessCvDatabase}}{{canAccessCvDatabase}}{{/defined}}
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/lib/soap-templates/UpdateRecruiterWithBillingID.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{#if recruiterName}}{{recruiterName}}{{/if}}
7 | {{#if customerBillingID}}{{customerBillingID}}{{/if}}
8 | {{#if contactFirstName}}{{contactFirstName}}{{/if}}
9 | {{#if contactLastName}}{{contactLastName}}{{/if}}
10 | {{#if contactLastName2}}{{contactLastName2}}{{/if}}
11 | {{#if address1}}{{address1}}{{/if}}
12 | {{#if address2}}{{address2}}{{/if}}
13 | {{#if address3}}{{address3}}{{/if}}
14 | {{#if townCity}}{{townCity}}{{/if}}
15 | {{#if countyState}}{{countyState}}{{/if}}
16 | {{#if zipPostCode}}{{zipPostCode}}{{/if}}
17 | {{#if country}}{{country}}{{/if}}
18 | {{#if telephone}}{{telephone}}{{/if}}
19 | {{#if contactEmail}}{{contactEmail}}{{/if}}
20 | {{recruiterTypeID}}
21 | {{#if image}}
22 |
23 | {{#if image.assetId}}{{image.assetId}}{{/if}}
24 | {{#if image.base64BlobString}}{{image.base64BlobString}}{{/if}}
25 |
26 | {{/if}}
27 | {{#if enhancedJobHtml}}
28 |
29 | {{#if enhancedJobHtml.jobTitleTextCss}}{{enhancedJobHtml.jobTitleTextCss}}{{/if}}
30 | {{#if enhancedJobHtml.jobHeadingsTextCss}}{{enhancedJobHtml.jobHeadingsTextCss}}{{/if}}
31 | {{#if enhancedJobHtml.buttonBackgroundCss}}{{enhancedJobHtml.buttonBackgroundCss}}{{/if}}
32 | {{#if enhancedJobHtml.buttonTextCss}}{{enhancedJobHtml.buttonTextCss}}{{/if}}
33 | {{#if enhancedJobHtml.hyperLinkCss}}{{enhancedJobHtml.hyperLinkCss}}{{/if}}
34 | {{#if enhancedJobHtml.banner}}
35 |
36 | {{#if enhancedJobHtml.banner.assetId}}{{enhancedJobHtml.banner.assetId}}{{/if}}
37 | {{#if enhancedJobHtml.banner.base64BlobString}}{{enhancedJobHtml.banner.base64BlobString}}{{/if}}
38 |
39 | {{/if}}
40 |
41 | {{/if}}
42 | {{#if accountManager}}
43 |
44 | {{#if accountManager.emailAddress}}{{accountManager.emailAddress}}{{/if}}
45 | {{#if accountManager.firstName}}{{accountManager.firstName}}{{/if}}
46 | {{#if accountManager.lastName}}{{accountManager.lastName}}{{/if}}
47 |
48 | {{/if}}
49 | {{#defined canAccessCvDatabase}}{{canAccessCvDatabase}}{{/defined}}
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/lib/soap-api-client.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | var fs = require('fs')
4 | var handlebars = require('handlebars')
5 | var format = require('util').format
6 | var path = require('path')
7 | var request = require('request')
8 | var xml2js = require('xml2js')
9 | var jsonPointer = require('json-pointer')
10 | var _ = require('lodash')
11 | var changeCase = require('change-case')
12 | var parseBooleans = require('./parseBooleans')
13 | var serviceDescription = require('./soap-api-service-description.json')
14 | var when = require('when')
15 |
16 | handlebars.registerHelper('defined', function(value, options) {
17 | return value !== undefined ? options.fn(this) : options.inverse(this)
18 | });
19 |
20 | module.exports = function(baseUrl) {
21 |
22 | var api = {}
23 |
24 | _.each(serviceDescription, function(apiConfig, apiName) {
25 |
26 | api[apiName] = {}
27 |
28 | _.each(apiConfig.services, function(serviceConfig, serviceName) {
29 | var source = fs.readFileSync(path.join(__dirname, 'soap-templates', serviceName + '.hbs'), {
30 | encoding: 'UTF-8'
31 | })
32 | var template = handlebars.compile(source)
33 | var url = baseUrl + apiConfig.path
34 |
35 | api[apiName][changeCase.camel(serviceName)] = function() {
36 | var args = Array.prototype.slice.call(arguments)
37 | var next = args.pop()
38 | var data = typeof next !== 'function' ? next : args.pop()
39 | var getResponse = function getResponse(handleResponse) {
40 | request({
41 | url: url,
42 | method: 'POST',
43 | headers: {
44 | 'Content-Type': 'text/xml'
45 | },
46 | body: template(data)
47 | }, function(err, response, body) {
48 | if (err) return handleResponse(new Error(format('POST %s failed. Original error was: %s', url, err.message)))
49 | if (!/^2/.test(response.statusCode)) return handleResponse(toError(url, response, body))
50 | new xml2js.Parser({
51 | trim: true,
52 | explicitArray: false,
53 | tagNameProcessors: [xml2js.processors.firstCharLowerCase, lowerCase('id')],
54 | attrNameProcessors: [xml2js.processors.firstCharLowerCase],
55 | valueProcessors: [xml2js.processors.parseNumbers, parseBooleans]
56 | }).parseString(body, function(err, obj) {
57 | if (err) return handleResponse(err)
58 | if (serviceConfig.forceArray) {
59 | var item = jsonPointer.get(obj, serviceConfig.result)
60 | if (!_.isArray(item)) jsonPointer.set(obj, serviceConfig.result, [item])
61 | }
62 | handleResponse(null, jsonPointer.get(obj, serviceConfig.result))
63 | })
64 | })
65 | }
66 |
67 | if (next && typeof next === 'function') {
68 | return getResponse(next)
69 | }
70 |
71 | return when.promise(function(resolve, reject) {
72 | return getResponse(function(err, results) {
73 | return (err) ? reject(err) : resolve(results);
74 | })
75 | });
76 | }
77 | })
78 | })
79 |
80 | function toError(url, response, body) {
81 | var err = new Error(format('POST %s failed. Status code was: %d', url, response.statusCode))
82 | err.statusCode = response.statusCode
83 | new xml2js.Parser({
84 | trim: true,
85 | explicitArray: false
86 | }).parseString(body, function(parseErr, data) {
87 | if (parseErr) return
88 | if (!jsonPointer.has(data, '/soap:Envelope/soap:Body/soap:Fault')) return
89 | err.faultCode = jsonPointer.get(data, '/soap:Envelope/soap:Body/soap:Fault/faultcode')
90 | err.faultString = jsonPointer.get(data, '/soap:Envelope/soap:Body/soap:Fault/faultstring')
91 | })
92 | return err
93 | }
94 |
95 |
96 | function lowerCase() {
97 | var args = Array.prototype.slice.call(arguments)
98 | return function(str) {
99 | return args.indexOf(str.toLowerCase()) >= 0 ? str.toLowerCase() : str
100 | }
101 | }
102 |
103 | return api
104 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # node-madgex
2 | [](https://travis-ci.org/guidesmiths/node-madgex)
3 |
4 | A node.js client for [Madgex](http://madgex.com) web services.
5 |
6 | ## About
7 | Madgex's web services are split between RESTful and SOAP APIs. This module currently supports only a subset of the APIs, but we would be delighted to receive pull requests for the methods that are missing.
8 |
9 | The current set of supported web services is
10 |
11 | ### REST API
12 |
13 | 1. getinfo
14 | 1. employer
15 | 1. myjobs
16 |
17 | ### Billing API
18 |
19 | 1. AddBilledJob
20 | 1. AddRecruiterV2
21 | 1. GetCategories
22 | 1. GetCategoryTerms
23 | 1. GetLocations
24 | 1. UpdateBilledJob
25 | 1. UpdateRecruiterWithBillingID
26 |
27 |
28 | ## The REST API
29 |
30 | ### Usage
31 |
32 | ```javascript
33 | var madgex = require('node-madgex')
34 | var client = madgex.createClient('http://yoursite-webservice.madgexjbtest.com', { key: 'yourkey', secret: 'yoursecret' })
35 |
36 | client.restApi.jobinfo({ jobid: 1257 }, function(err, data) {
37 | console.log(data);
38 | })
39 | ```
40 |
41 | API methods usually accept a params hash and a completion callback with (err, data, result) signature;
42 |
43 | ### Promises
44 | As an alternative to the completion callback you can use promises as well. Api methods return with a promise
45 | that resolves after the completion callback (if one is present).
46 |
47 | ```javascript
48 | client.jobinfo({ jobid: 1257 })
49 | .then(function(data) {
50 | //handle data
51 | })
52 | .fail(function(err) {
53 | //dome something with the error
54 | });
55 | ```
56 |
57 | #### Chain'em
58 |
59 | Promised values are easy to compose:
60 | ```javascript
61 | client.jobinfo
62 | .search({})
63 | .then(function(jobs) { return client.jobinfo({jobid: jobs[0].id }) })
64 | .then(function(jobdetails) { /*handle data*/ })
65 | .fail(function(err) { /*dome something with the error */ });
66 | ```
67 |
68 | ### Service Description
69 | The RESTful client API is dynamically built by code from the service description config file.
70 | Extend this to add new functions to the API. (/lib/rest-api-service-description.json)
71 |
72 | ### Service Methods
73 |
74 | #### jobinfo(params, done)
75 | Displays information about a job
76 |
77 | ##### params
78 | a hash with the following fields
79 |
80 | field | type,info
81 | --- | ---
82 | jobid | integer, required
83 |
84 | #### jobinfo.search(params, done)
85 | Searches in the job database
86 |
87 | ##### params
88 | field | type,info
89 | --- | ---
90 | keywords | free text with boolean expressions allowed, optional
91 | dateFrom | ISO format date
92 | dateTo | ISO format date
93 |
94 | ...and much more. refer to the Madgex REST documentation for full set of params.
95 |
96 |
97 | #### jobinfo.search.full(params, done)
98 | Same as search but returns full dataset
99 |
100 | #### jobinfo.search.facets(params, done)
101 | Return search refiners for a search result. Params are same as in search()
102 |
103 | #### employer(params, done)
104 | Displays information about am employer
105 |
106 | ##### params
107 | a hash with the following fields
108 |
109 | field | type,info
110 | --- | ---
111 | id | integer, required
112 |
113 | #### employer.search(params, done)
114 | Searches in the employer database
115 |
116 | #### myjobs.add(params, done)
117 |
118 | #### myjobs.delete(params, done)
119 |
120 | ## The SOAP API
121 |
122 | ### Usage
123 | ```javascript
124 | var madgex = require('node-madgex')
125 | var client = madgex.createClient('http://yoursite-webservice.madgexjbtest.com', { key: 'yourkey', secret: 'yoursecret' })
126 |
127 | client.soapApi.billingApi.getCategoryTerms({ categoryId: 105 }, function(err, data) {
128 | console.log(data);
129 | })
130 | ```
131 | Each billingApi method takes an optional parameters object and typical callback. You can determine the available parameters names by inspecting the equivalent methods handlebars template (see ./lib/soap-templates/*.hbs). These are camel cased equivalents to the elements specified in the Madgex Billing API documentation. Working out which parameters are required and what their values should be requires a degree of experience.
132 |
133 | Responses stripped of their SOAPiness and converted to camelCased json. Integers, floats and booleans are parsed, so instead of
134 |
135 | ```xml
136 |
137 |
138 |
139 |
140 |
141 | false
142 | true
143 | 105
144 | Hours
145 |
146 |
147 |
148 |
149 |
150 | ```
151 |
152 | you'll receive
153 | ```json
154 | [
155 | {
156 | "mandatory": false,
157 | "multiSelect": true,
158 | "id": 105,
159 | "name": "hours"
160 | }
161 | ]
162 | ```
163 |
164 | #### Error handling
165 | In the event of an HTTP error, the err object passed to your callback will be blessed with a 'statusCode' property. In the event ofa SOAP Fault, the err object will additionally be blessed with 'faultCode' and 'faultString' properties.
166 |
167 | #### Adding more SOAP API methods
168 | Adding more API methods is easy
169 |
170 | 1. Fork and clone node-madgex
171 | 2. Generate a real request and response using your tool of choice ([SoapUI](http://www.soapui.org/), curl, etc)
172 | 3. Convert the request to a handlbars template and save it in lib/soap-templates/MethodName.hbs,
173 | 4. Save the response in test/replies/soap/MethodName.ok.xml
174 | 5. Update lib/soap-api-service-description.json
175 | 6. Add one or more test cases
176 | 7. Submit a PR
177 |
178 |
--------------------------------------------------------------------------------
/test/replies/soap/GetLocations.many.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 325
7 |
8 | London (Greater)
9 | GB
10 | 5
11 |
12 |
13 | 1500
14 |
15 | London (Central)
16 | GB
17 | 6
18 |
19 |
20 | 289
21 |
22 | Londonderry
23 | GB
24 | 5
25 |
26 |
27 | 5920616
28 |
29 | London (East)
30 | GB
31 | 6
32 |
33 |
34 | 5920618
35 |
36 | London (West)
37 | GB
38 | 6
39 |
40 |
41 | 5920615
42 |
43 | London (North)
44 | GB
45 | 6
46 |
47 |
48 | 5920617
49 |
50 | London (South)
51 | GB
52 | 6
53 |
54 |
55 | 538
56 |
57 | Longhope
58 | GB
59 | 8
60 |
61 |
62 | 1819
63 |
64 | Longfield
65 | GB
66 | 8
67 |
68 |
69 | 1268
70 |
71 | Longniddry
72 | GB
73 | 8
74 |
75 |
76 | 660
77 |
78 | Londonderry
79 | GB
80 | 8
81 |
82 |
83 | 1784215
84 |
85 | London (City of)
86 | GB
87 | 9
88 |
89 |
90 | 13542
91 |
92 | Lonlas
93 | GB
94 | 10
95 |
96 |
97 | 5172
98 |
99 | Lonmay
100 | GB
101 | 10
102 |
103 |
104 | 26439
105 |
106 | Longcot
107 | GB
108 | 10
109 |
110 |
111 | 10374
112 |
113 | Longden
114 | GB
115 | 10
116 |
117 |
118 | 3122
119 |
120 | Longdon
121 | GB
122 | 10
123 |
124 |
125 | 16462
126 |
127 | Longdon
128 | GB
129 | 10
130 |
131 |
132 | 16313
133 |
134 | Longham
135 | GB
136 | 10
137 |
138 |
139 | 17116
140 |
141 | Longney
142 | GB
143 | 10
144 |
145 |
146 |
147 |
148 |
--------------------------------------------------------------------------------
/test/rest-api-tests.js:
--------------------------------------------------------------------------------
1 | var madgex = require('../index.js'),
2 | assert = require('assert'),
3 | fs = require('fs'),
4 | path = require('path'),
5 | nock = require('nock')
6 |
7 | describe('Madgex Client REST API', function () {
8 |
9 | this.timeout(5000);
10 |
11 | var client = madgex.createClient('http://guidesmiths-webservice.madgexjbtest.com', {
12 | key: "madgex-test-key",
13 | secret: "madgex-test-secret"
14 | }).restApi;
15 |
16 | it("should have a jobInfo API part", function () {
17 | assert.ok(client.jobinfo, "jobInfo api branch not found");
18 | });
19 |
20 | it("should provide a path info from method invocation", function () {
21 |
22 | assert.ok(client.jobinfo.getPathAndQuery, "method has not path discover method");
23 | assert.equal(client.jobinfo.getPathAndQuery({}), "/restapi/jobinfo?deviceId=node-device",
24 | "method path resolution is incorrect");
25 | });
26 |
27 | describe("jobinfo API function", function () {
28 | it("shoud be invokable", function () {
29 | assert.equal(typeof client.jobinfo, "function", "jobinfo is not a function");
30 | })
31 | it("should return info on a job", function (done) {
32 |
33 | nock('http://guidesmiths-webservice.madgexjbtest.com:80')
34 | .get('/restapi/jobinfo?jobid=1257&deviceId=node-device')
35 | .replyWithFile(200, __dirname + '/replies/rest/get.jobinfo.jobid.json')
36 |
37 | client.jobinfo({ jobid: 1257 }, function (err, data) {
38 | assert.equal(null, err, "error is not null");
39 | assert.equal(data.id, 1257, "jobid id does not match");
40 | done();
41 | });
42 | })
43 |
44 | it("should have a search function", function () {
45 | assert.ok(client.jobinfo, "jobInfo api branch not found");
46 | });
47 | describe("jobinfo.search API function ", function () {
48 | it("should return 30 items without params", function (done) {
49 |
50 | nock('http://guidesmiths-webservice.madgexjbtest.com:80')
51 | .get('/restapi/jobinfo/search?deviceId=node-device')
52 | .replyWithFile(200, __dirname + '/replies/rest/get.jobinfo.search.json')
53 |
54 | client.jobinfo.search({}, function(err, data) {
55 | assert.ok(data.jobs, "search yielded not jobs");
56 | assert.equal(data.jobs.length, 30, "search resulted incorred number of items");
57 | done();
58 | });
59 | });
60 | it("should also return a promise the resolves to 30 jobs", function (done) {
61 |
62 | nock('http://guidesmiths-webservice.madgexjbtest.com:80')
63 | .get('/restapi/jobinfo/search?deviceId=node-device')
64 | .replyWithFile(200, __dirname + '/replies/rest/get.jobinfo.search.json')
65 |
66 | var jobs = client.jobinfo.search({});
67 | assert.ok(jobs.then, "result is not a promise");
68 | jobs.then(function (data) {
69 | assert.equal(data.jobs.length, 30, "item count mismatch");
70 | done();
71 | });
72 | })
73 | it("should have a full subfunction", function () {
74 | assert.ok(client)
75 | });
76 | it("should have a facets subfunction", function () {
77 | assert.ok(client)
78 | });
79 |
80 | describe("jobinfo.search.full API function", function () {
81 | it("should return 30 items without params", function (done) {
82 |
83 | nock('http://guidesmiths-webservice.madgexjbtest.com:80')
84 | .get('/restapi/jobinfo/search/full?deviceId=node-device')
85 | .replyWithFile(200, __dirname + '/replies/rest/get.jobinfo.search.full.json')
86 |
87 | client.jobinfo.search.full({}, function (err, data) {
88 | assert.equal(data.jobs.length, 30, "jobs item count mismatch")
89 | done();
90 | });
91 | });
92 |
93 | });
94 |
95 | describe("jobinfo.search.facets API function", function () {
96 | it("should return jobType facet with 20 items without params", function (done) {
97 |
98 | nock('http://guidesmiths-webservice.madgexjbtest.com:80')
99 | .get('/restapi/jobinfo/search/facets?deviceId=node-device')
100 | .replyWithFile(200, __dirname + '/replies/rest/get.jobinfo.search.facets.json')
101 |
102 |
103 | client.jobinfo.search.facets({}, function (err, data) {
104 | assert.ok(data.jobType.length >= 20, "jobs item count mismatch")
105 | //done();
106 | //console.log(data.jobType.length);
107 | done();
108 | });
109 | });
110 | });
111 | });
112 | });
113 |
114 | it("should have an employer API part", function () {
115 | assert.ok(client.employer, "employer api branch not found");
116 | });
117 |
118 | describe("employer API function", function () {
119 | it("should be invokable", function () {
120 |
121 | });
122 | it("should have a search function", function () {
123 | assert.equal(typeof client.employer,"function", "employer must be a function");
124 | });
125 |
126 | describe("employer.search", function () {
127 | it("should find employers without params", function (done) {
128 |
129 | nock('http://guidesmiths-webservice.madgexjbtest.com:80')
130 | .get('/restapi/employer/search?deviceId=node-device')
131 | .replyWithFile(200, __dirname + '/replies/rest/get.employer.search.json')
132 |
133 | client.employer.search({}, function (err, data) {
134 | assert.equal(data.employers.length, 30, "employers count mismatch");
135 | done();
136 | });
137 | });
138 |
139 | })
140 | });
141 |
142 |
143 | it("should have a myjobs API part", function () {
144 | assert.ok(client.myjobs, "api part is missing");
145 | });
146 |
147 | describe("myjobs API part", function () {
148 | it("should provide the 'add' method", function () {
149 | assert.ok(client.myjobs.add, "add function not found");
150 | });
151 | it("should provide the 'delete' method", function () {
152 | assert.ok(client.myjobs.delete, "add function not found");
153 | });
154 | });
155 | })
156 |
157 |
158 |
--------------------------------------------------------------------------------
/test/replies/rest/get.jobinfo.search.facets.json:
--------------------------------------------------------------------------------
1 | {
2 | "status": "ok",
3 | "result": {
4 | "totalResults": 132,
5 | "recruiterType": [
6 | {
7 | "id": -20,
8 | "name": "Recruitment Consultant",
9 | "count": 1
10 | },
11 | {
12 | "id": -10,
13 | "name": "Direct Employer",
14 | "count": 132
15 | }
16 | ],
17 | "academicDiscipline": [
18 | {
19 | "id": 1,
20 | "name": "Agriculture, Food and Veterinary",
21 | "count": 6
22 | },
23 | {
24 | "id": 2,
25 | "name": "Architecture, Building and Planning",
26 | "count": 5
27 | },
28 | {
29 | "id": 3,
30 | "name": "Biological Sciences",
31 | "count": 8
32 | },
33 | {
34 | "id": 4,
35 | "name": "Business and Management Studies",
36 | "count": 16
37 | },
38 | {
39 | "id": 5,
40 | "name": "Computer Science",
41 | "count": 11
42 | },
43 | {
44 | "id": 6,
45 | "name": "Creative Arts and Design",
46 | "count": 12
47 | },
48 | {
49 | "id": 7,
50 | "name": "Economics",
51 | "count": 8
52 | },
53 | {
54 | "id": 8,
55 | "name": "Education Studies",
56 | "count": 8
57 | },
58 | {
59 | "id": 9,
60 | "name": "Engineering and Technology",
61 | "count": 13
62 | },
63 | {
64 | "id": 10,
65 | "name": "Health and Medical",
66 | "count": 8
67 | },
68 | {
69 | "id": 11,
70 | "name": "Historical and Philosophical Studies",
71 | "count": 5
72 | },
73 | {
74 | "id": 12,
75 | "name": "Information Management and Librarianship",
76 | "count": 7
77 | },
78 | {
79 | "id": 13,
80 | "name": "Languages, Literature and Culture",
81 | "count": 13
82 | },
83 | {
84 | "id": 14,
85 | "name": "Law",
86 | "count": 8
87 | },
88 | {
89 | "id": 15,
90 | "name": "Mathematics and Statistics",
91 | "count": 8
92 | },
93 | {
94 | "id": 16,
95 | "name": "Media and Communications",
96 | "count": 12
97 | },
98 | {
99 | "id": 17,
100 | "name": "Physical and Environmental Sciences",
101 | "count": 4
102 | },
103 | {
104 | "id": 18,
105 | "name": "Politics and Government",
106 | "count": 9
107 | },
108 | {
109 | "id": 19,
110 | "name": "Psychology",
111 | "count": 10
112 | },
113 | {
114 | "id": 20,
115 | "name": "Social Sciences and Social Care",
116 | "count": 8
117 | },
118 | {
119 | "id": 21,
120 | "name": "Sport and Leisure",
121 | "count": 6
122 | }
123 | ],
124 | "jobType": [
125 | {
126 | "id": 22,
127 | "name": "Senior Management & Heads of Department",
128 | "count": 51
129 | },
130 | {
131 | "id": 23,
132 | "name": "Vice-chancellors \/ Principals \/ Provosts",
133 | "count": 3
134 | },
135 | {
136 | "id": 24,
137 | "name": "Deputy Vice-chancellors",
138 | "count": 7
139 | },
140 | {
141 | "id": 25,
142 | "name": "Pro Vice-chancellors",
143 | "count": 4
144 | },
145 | {
146 | "id": 26,
147 | "name": "Deans",
148 | "count": 6
149 | },
150 | {
151 | "id": 27,
152 | "name": "Heads of Department",
153 | "count": 11
154 | },
155 | {
156 | "id": 28,
157 | "name": "Chief Operating Officers",
158 | "count": 4
159 | },
160 | {
161 | "id": 29,
162 | "name": "Registrars \/ Assistant Registrars",
163 | "count": 3
164 | },
165 | {
166 | "id": 30,
167 | "name": "Directors",
168 | "count": 13
169 | },
170 | {
171 | "id": 31,
172 | "name": "Other Senior Management",
173 | "count": 9
174 | },
175 | {
176 | "id": 32,
177 | "name": "Academic Posts",
178 | "count": 67
179 | },
180 | {
181 | "id": 33,
182 | "name": "Professors \/ Chairs",
183 | "count": 8
184 | },
185 | {
186 | "id": 34,
187 | "name": "Readers \/ Senior Research Fellows",
188 | "count": 5
189 | },
190 | {
191 | "id": 35,
192 | "name": "Principal \/ Senior Lecturers \/ Associate Professors",
193 | "count": 25
194 | },
195 | {
196 | "id": 36,
197 | "name": "Lecturers \/ Assistant Professors",
198 | "count": 28
199 | },
200 | {
201 | "id": 37,
202 | "name": "Tutors",
203 | "count": 1
204 | },
205 | {
206 | "id": 38,
207 | "name": "Fellowships",
208 | "count": 4
209 | },
210 | {
211 | "id": 39,
212 | "name": "Postdocs",
213 | "count": 5
214 | },
215 | {
216 | "id": 40,
217 | "name": "Studentships",
218 | "count": 9
219 | },
220 | {
221 | "id": 41,
222 | "name": "Other Academic",
223 | "count": 2
224 | },
225 | {
226 | "id": 42,
227 | "name": "Academic Related",
228 | "count": 16
229 | },
230 | {
231 | "id": 43,
232 | "name": "Librarians",
233 | "count": 2
234 | },
235 | {
236 | "id": 44,
237 | "name": "Research Assistants",
238 | "count": 4
239 | },
240 | {
241 | "id": 45,
242 | "name": "Technicians",
243 | "count": 5
244 | },
245 | {
246 | "id": 46,
247 | "name": "Other Academic Related",
248 | "count": 2
249 | },
250 | {
251 | "id": 47,
252 | "name": "Professional Services",
253 | "count": 30
254 | },
255 | {
256 | "id": 48,
257 | "name": "Administrative",
258 | "count": 3
259 | },
260 | {
261 | "id": 49,
262 | "name": "Estates and Facilities",
263 | "count": 2
264 | },
265 | {
266 | "id": 50,
267 | "name": "Finance and Procurement",
268 | "count": 6
269 | },
270 | {
271 | "id": 51,
272 | "name": "Fundraising, Development and Alumni",
273 | "count": 8
274 | },
275 | {
276 | "id": 52,
277 | "name": "Human Resources",
278 | "count": 4
279 | },
280 | {
281 | "id": 53,
282 | "name": "International Activities",
283 | "count": 2
284 | },
285 | {
286 | "id": 54,
287 | "name": "IT Services",
288 | "count": 1
289 | },
290 | {
291 | "id": 55,
292 | "name": "Knowledge Transfer \/ Business Links",
293 | "count": 4
294 | },
295 | {
296 | "id": 56,
297 | "name": "Library \/ Records \/ Information Management",
298 | "count": 2
299 | },
300 | {
301 | "id": 57,
302 | "name": "Marketing and Communications",
303 | "count": 3
304 | },
305 | {
306 | "id": 58,
307 | "name": "Planning ",
308 | "count": 3
309 | },
310 | {
311 | "id": 59,
312 | "name": "Registry",
313 | "count": 3
314 | },
315 | {
316 | "id": 60,
317 | "name": "Student Recruitment \/ Admissions",
318 | "count": 2
319 | },
320 | {
321 | "id": 61,
322 | "name": "Student Services",
323 | "count": 1
324 | },
325 | {
326 | "id": 62,
327 | "name": "Other Professional Services",
328 | "count": 2
329 | }
330 | ],
331 | "salaryBand": [
332 | {
333 | "id": 63,
334 | "name": "\u00a30-\u00a310,000",
335 | "count": 5
336 | },
337 | {
338 | "id": 64,
339 | "name": "\u00a310,001 - \u00a320,000",
340 | "count": 2
341 | },
342 | {
343 | "id": 65,
344 | "name": "\u00a320,001 - \u00a330,000",
345 | "count": 3
346 | },
347 | {
348 | "id": 66,
349 | "name": "\u00a330,001 - \u00a340,000",
350 | "count": 6
351 | },
352 | {
353 | "id": 67,
354 | "name": "\u00a340,001 - \u00a350,000",
355 | "count": 1
356 | },
357 | {
358 | "id": 68,
359 | "name": "\u00a350,001 - \u00a360,000",
360 | "count": 2
361 | },
362 | {
363 | "id": 70,
364 | "name": "\u00a370,001 - \u00a380,000",
365 | "count": 2
366 | },
367 | {
368 | "id": 71,
369 | "name": "\u00a380,001 - \u00a390,000",
370 | "count": 1
371 | },
372 | {
373 | "id": 72,
374 | "name": "\u00a390,001 - \u00a3100,000",
375 | "count": 3
376 | },
377 | {
378 | "id": 73,
379 | "name": "\u00a3100,001 - \u00a3110,000",
380 | "count": 4
381 | },
382 | {
383 | "id": 74,
384 | "name": "\u00a3110,001 - \u00a3120,000",
385 | "count": 4
386 | },
387 | {
388 | "id": 75,
389 | "name": "\u00a3120,001 - \u00a3130,000",
390 | "count": 1
391 | },
392 | {
393 | "id": 76,
394 | "name": "\u00a3130,001 - \u00a3140,000",
395 | "count": 6
396 | },
397 | {
398 | "id": 77,
399 | "name": "\u00a3140,001 - \u00a3150,000",
400 | "count": 2
401 | },
402 | {
403 | "id": 78,
404 | "name": "\u00a3150,001 - \u00a3160,000",
405 | "count": 2
406 | },
407 | {
408 | "id": 80,
409 | "name": "\u00a3170,001 - \u00a3180,000",
410 | "count": 1
411 | },
412 | {
413 | "id": 81,
414 | "name": "\u00a3180,001 - \u00a3190,000",
415 | "count": 3
416 | },
417 | {
418 | "id": 82,
419 | "name": "\u00a3190,001 - \u00a3200,000",
420 | "count": 3
421 | },
422 | {
423 | "id": 84,
424 | "name": "Daily Rate",
425 | "count": 5
426 | },
427 | {
428 | "id": 85,
429 | "name": "Hourly Rate",
430 | "count": 3
431 | }
432 | ],
433 | "contractType": [
434 | {
435 | "id": 86,
436 | "name": "Permanent",
437 | "count": 105
438 | },
439 | {
440 | "id": 87,
441 | "name": "Temporary",
442 | "count": 23
443 | },
444 | {
445 | "id": 88,
446 | "name": "Fixed Term",
447 | "count": 25
448 | },
449 | {
450 | "id": 89,
451 | "name": "Maternity Cover",
452 | "count": 19
453 | }
454 | ],
455 | "hours": [
456 | {
457 | "id": 90,
458 | "name": "Full Time",
459 | "count": 110
460 | },
461 | {
462 | "id": 91,
463 | "name": "Part Time",
464 | "count": 28
465 | },
466 | {
467 | "id": 92,
468 | "name": "Sessional",
469 | "count": 28
470 | }
471 | ],
472 | "location": [
473 |
474 | ]
475 | }
476 | }
--------------------------------------------------------------------------------
/test/replies/rest/get.employer.search.json:
--------------------------------------------------------------------------------
1 | {
2 | "status": "ok",
3 | "result": {
4 | "totalResults": 45,
5 | "startIndex": 0,
6 | "itemsPerPage": 30,
7 | "employers": [
8 | {
9 | "id": 1171,
10 | "title": "Anglia Ruskin University",
11 | "noOfLiveJobs": 1,
12 | "shortDescription": "",
13 | "recruiterLogoId": "53fb405d-c4af-4b3f-83f8-94d32fbdedfe",
14 | "employerTypes": [
15 | "Direct Employer"
16 | ],
17 | "noOfVideos": 0
18 | },
19 | {
20 | "id": 7,
21 | "title": "Apple (Madgex Test Data)",
22 | "noOfLiveJobs": 1,
23 | "shortDescription": "Are you about to graduate from college or graduate school? Did you graduate in the past year? Apple is hiring new grads.",
24 | "recruiterLogoId": "3999c39f-5794-4a05-a969-19b1abe032f8",
25 | "employerTypes": [
26 | "Direct Employer"
27 | ],
28 | "noOfVideos": 0
29 | },
30 | {
31 | "id": 8,
32 | "title": "Atari Group (Madgex Test Data)",
33 | "noOfLiveJobs": 2,
34 | "shortDescription": "Atari is one of the world's truly iconic gaming brands. Today we are transforming Atari into a 21st century digital entertainment company.",
35 | "recruiterLogoId": "0d153a46-d60c-42db-8f72-153bd2365fb6",
36 | "employerTypes": [
37 | "Direct Employer"
38 | ],
39 | "noOfVideos": 0
40 | },
41 | {
42 | "id": 1202,
43 | "title": "Bath Spa University",
44 | "noOfLiveJobs": 3,
45 | "shortDescription": "",
46 | "recruiterLogoId": "5fe21803-d4ce-4685-834d-b55555071cbb",
47 | "employerTypes": [
48 | "Direct Employer"
49 | ],
50 | "noOfVideos": 0
51 | },
52 | {
53 | "id": 9,
54 | "title": "BBC (Madgex Test Data)",
55 | "noOfLiveJobs": 1,
56 | "shortDescription": "It\u2019s our goal to become the most creative organisation in the world. In order to make it a reality, we need to keep on attracting the brightest, most creative and talented people.",
57 | "recruiterLogoId": "58ac51bc-a31d-48bc-b4ba-b53766d9f7f8",
58 | "employerTypes": [
59 | "Direct Employer"
60 | ],
61 | "noOfVideos": 0
62 | },
63 | {
64 | "id": 1295,
65 | "title": "Bournemouth University",
66 | "noOfLiveJobs": 1,
67 | "shortDescription": "",
68 | "recruiterLogoId": "9a019be4-0723-4757-bb83-fe61aa64f082",
69 | "employerTypes": [
70 | "Direct Employer"
71 | ],
72 | "noOfVideos": 0
73 | },
74 | {
75 | "id": 1180,
76 | "title": "Edge Hill University",
77 | "noOfLiveJobs": 4,
78 | "shortDescription": "",
79 | "recruiterLogoId": "f2c7218f-d2e7-4e1f-b9c4-cfb6cca57b39",
80 | "employerTypes": [
81 | "Direct Employer"
82 | ],
83 | "noOfVideos": 0
84 | },
85 | {
86 | "id": 1251,
87 | "title": "Edinburgh Business School",
88 | "noOfLiveJobs": 3,
89 | "shortDescription": "",
90 | "recruiterLogoId": "0fa5974c-0b4b-496e-8672-43f394608f5c",
91 | "employerTypes": [
92 | "Direct Employer"
93 | ],
94 | "noOfVideos": 0
95 | },
96 | {
97 | "id": 1249,
98 | "title": "Edinburgh Napier University",
99 | "noOfLiveJobs": 2,
100 | "shortDescription": "",
101 | "recruiterLogoId": "65d93e49-51fe-466e-82e2-6bb0336eda25",
102 | "employerTypes": [
103 | "Direct Employer"
104 | ],
105 | "noOfVideos": 0
106 | },
107 | {
108 | "id": 1109,
109 | "title": "Eduardo Frost Ltd",
110 | "noOfLiveJobs": 0,
111 | "shortDescription": "Best recruiter in town",
112 | "recruiterLogoId": "6ca45d4e-88eb-43d7-b86e-3760bf0256fa",
113 | "employerTypes": [
114 | "Direct Employer"
115 | ],
116 | "noOfVideos": 0
117 | },
118 | {
119 | "id": 1206,
120 | "title": "Elsevier",
121 | "noOfLiveJobs": 1,
122 | "shortDescription": "",
123 | "recruiterLogoId": "9d7aed14-20c1-4ad4-ac31-ce5b243a8527",
124 | "employerTypes": [
125 | "Direct Employer"
126 | ],
127 | "noOfVideos": 0
128 | },
129 | {
130 | "id": 11,
131 | "title": "Future (Madgex Test Data)",
132 | "noOfLiveJobs": 1,
133 | "shortDescription": "As an international media business which produces over 100 magazines each month alongside 70 websites and 16 annual live events, we can offer a wealth of career opportunities for people with the right skills and passion to move our business forward.",
134 | "recruiterLogoId": "05ccb940-5db7-48eb-afd2-4f444e9f37fe",
135 | "employerTypes": [
136 | "Direct Employer"
137 | ],
138 | "noOfVideos": 0
139 | },
140 | {
141 | "id": 12,
142 | "title": "Google (Madgex Test Data)",
143 | "noOfLiveJobs": 3,
144 | "shortDescription": "Chances are you have a good idea of where you want to go in life. At Google, we\u2019ve designed a culture that helps you get there.",
145 | "recruiterLogoId": "9ee7f0f3-6348-4604-9a56-ba03446de12c",
146 | "employerTypes": [
147 | "Direct Employer"
148 | ],
149 | "noOfVideos": 0
150 | },
151 | {
152 | "id": 13,
153 | "title": "Hilton (Madgex Test Data)",
154 | "noOfLiveJobs": 1,
155 | "shortDescription": "'Only those who feel valued can truly add value.' This is the ethos behind every decision we make at Hilton. We are immensely proud of the culture of support and sense of belonging that we all feel when we work within The Hilton Family of Hotels.",
156 | "recruiterLogoId": "3e053738-f8aa-40f9-be28-d35c4a436fa8",
157 | "employerTypes": [
158 | "Direct Employer"
159 | ],
160 | "noOfVideos": 0
161 | },
162 | {
163 | "id": 14,
164 | "title": "HSBC Bank plc (Madgex Test Data)",
165 | "noOfLiveJobs": 2,
166 | "shortDescription": "Is a long-term career decision about waiting for the right moment to come along? Or is it about making the right move, now? Particularly in these times, we believe in making the most of every second. That's what makes us who we are today.",
167 | "recruiterLogoId": "500312f7-a429-4559-b70d-d605d8a0486a",
168 | "employerTypes": [
169 | "Direct Employer"
170 | ],
171 | "noOfVideos": 0
172 | },
173 | {
174 | "id": 1117,
175 | "title": "Kings College London",
176 | "noOfLiveJobs": 6,
177 | "shortDescription": "King's is one of the world's leading research and teaching universities based in the heart of London.",
178 | "recruiterLogoId": "260374d2-32db-40b4-a7c7-959bf52a03f9",
179 | "employerTypes": [
180 | "Direct Employer"
181 | ],
182 | "noOfVideos": 0
183 | },
184 | {
185 | "id": 1200,
186 | "title": "Liverpool Institute for Performing Arts",
187 | "noOfLiveJobs": 1,
188 | "shortDescription": "",
189 | "recruiterLogoId": "1ee66178-1492-4a5c-a0b3-adc97eaab7a6",
190 | "employerTypes": [
191 | "Direct Employer"
192 | ],
193 | "noOfVideos": 0
194 | },
195 | {
196 | "id": 1236,
197 | "title": "London School of Economic & Political Science",
198 | "noOfLiveJobs": 2,
199 | "shortDescription": "",
200 | "recruiterLogoId": "4a566a38-2cd6-43ad-97e0-08733cd04c13",
201 | "employerTypes": [
202 | "Direct Employer"
203 | ],
204 | "noOfVideos": 0
205 | },
206 | {
207 | "id": 15,
208 | "title": "Marriott Hotels (Madgex Test Data)",
209 | "noOfLiveJobs": 1,
210 | "shortDescription": "You have already found the perfect place to pursue your professional career. Ready to be challenged? Ready to achieve and be recognised? You're on the road to success and we invite you to explore our established global company full of \"people going places",
211 | "recruiterLogoId": "d26e36da-6874-4f36-a183-5a34b1463424",
212 | "employerTypes": [
213 | "Direct Employer"
214 | ],
215 | "noOfVideos": 0
216 | },
217 | {
218 | "id": 1255,
219 | "title": "Newman University",
220 | "noOfLiveJobs": 2,
221 | "shortDescription": "",
222 | "recruiterLogoId": "6583fa39-0812-4142-b3c6-d8662b488790",
223 | "employerTypes": [
224 | "Direct Employer"
225 | ],
226 | "noOfVideos": 0
227 | },
228 | {
229 | "id": 16,
230 | "title": "Oxfam (Madgex Test Data)",
231 | "noOfLiveJobs": 8,
232 | "shortDescription": "Oxfam GB employs more than 6,000 people in 80 different countries who all share the commitment to work together to end poverty and suffering.",
233 | "recruiterLogoId": "f1b42b0e-f05e-4052-884c-6e2d33ff58dc",
234 | "employerTypes": [
235 | "Direct Employer"
236 | ],
237 | "noOfVideos": 0
238 | },
239 | {
240 | "id": 1253,
241 | "title": "Queen Mary, University of London",
242 | "noOfLiveJobs": 1,
243 | "shortDescription": "",
244 | "recruiterLogoId": "93988ae0-66ee-4c9d-a852-bff2fd53eeeb",
245 | "employerTypes": [
246 | "Direct Employer"
247 | ],
248 | "noOfVideos": 0
249 | },
250 | {
251 | "id": 1208,
252 | "title": "RMIT University",
253 | "noOfLiveJobs": 4,
254 | "shortDescription": "",
255 | "recruiterLogoId": "6d6dba39-945a-478a-9347-880fc9eaa8c0",
256 | "employerTypes": [
257 | "Direct Employer"
258 | ],
259 | "noOfVideos": 0
260 | },
261 | {
262 | "id": 18,
263 | "title": "Sainsbury's (Madgex Test Data)",
264 | "noOfLiveJobs": 5,
265 | "shortDescription": "We're looking for talented people to support our supermarkets, convenience stores and depots around the country. With everything from HR and Finance to Marketing and Buying under one roof, whatever your area of expertise, you\u2019ll find the right role for yo",
266 | "recruiterLogoId": "310ceb04-3dda-4e89-a9ca-0bc6f0e9dba9",
267 | "employerTypes": [
268 | "Direct Employer"
269 | ],
270 | "noOfVideos": 0
271 | },
272 | {
273 | "id": 19,
274 | "title": "Sky (Madgex Test Data)",
275 | "noOfLiveJobs": 12,
276 | "shortDescription": "At Sky, our job is to entertain people - and already more than 17 million viewers in 8 million households across the UK tune into our world-beating entertainment.",
277 | "recruiterLogoId": "d3031aac-8566-4227-ae58-c72811cc20e6",
278 | "employerTypes": [
279 | "Direct Employer"
280 | ],
281 | "noOfVideos": 0
282 | },
283 | {
284 | "id": 20,
285 | "title": "Smile (Madgex Test Data)",
286 | "noOfLiveJobs": 3,
287 | "shortDescription": "As smile continues to grow, we're constantly on the look-out for the right kind of people to join our team.",
288 | "recruiterLogoId": "6ca45d4e-88eb-43d7-b86e-3760bf0256fa",
289 | "employerTypes": [
290 | "Direct Employer"
291 | ],
292 | "noOfVideos": 0
293 | },
294 | {
295 | "id": 1187,
296 | "title": "SOAS University of London",
297 | "noOfLiveJobs": 1,
298 | "shortDescription": "",
299 | "recruiterLogoId": "59874867-677b-4a1b-9c5c-fe33c5fd9387",
300 | "employerTypes": [
301 | "Direct Employer"
302 | ],
303 | "noOfVideos": 0
304 | },
305 | {
306 | "id": 1215,
307 | "title": "Teesside University",
308 | "noOfLiveJobs": 13,
309 | "shortDescription": "",
310 | "recruiterLogoId": "cb7c6707-bb9e-464b-b168-e166d4f1e3d5",
311 | "employerTypes": [
312 | "Direct Employer"
313 | ],
314 | "noOfVideos": 0
315 | },
316 | {
317 | "id": 1133,
318 | "title": "TES Globetrotters",
319 | "noOfLiveJobs": 0,
320 | "shortDescription": "",
321 | "recruiterLogoId": "9ee7f0f3-6348-4604-9a56-ba03446de12c",
322 | "employerTypes": [
323 | "Direct Employer"
324 | ],
325 | "noOfVideos": 0
326 | },
327 | {
328 | "id": 1258,
329 | "title": "The British University in Egypt",
330 | "noOfLiveJobs": 3,
331 | "shortDescription": "",
332 | "recruiterLogoId": "492dc557-44f7-46c8-83f8-5ba20eaf9f2a",
333 | "employerTypes": [
334 | "Direct Employer"
335 | ],
336 | "noOfVideos": 0
337 | }
338 | ]
339 | }
340 | }
--------------------------------------------------------------------------------
/test/soap-api-tests.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | var madgex = require('../index.js')
4 | var assert = require('assert')
5 | var fs = require('fs')
6 | var path = require('path')
7 | var nock = require('nock')
8 | var cheerio = require ('cheerio')
9 |
10 |
11 | describe('Madgex SOAP API', function() {
12 | this.timeout(5000)
13 |
14 | var client = madgex.createClient('http://guidesmiths-webservice.madgexjbtest.com', {
15 | key: "madgex-test-key",
16 | secret: "madgex-test-secret"
17 | }).soapApi
18 |
19 | describe('Billing API', function() {
20 |
21 | it('should report errors', function(done) {
22 |
23 | var scope = nock('http://guidesmiths-webservice.madgexjbtest.com')
24 | .post('/billing.asmx')
25 | .replyWithError('Test Error')
26 |
27 | client.billingApi.getCategories(function(err, results) {
28 | assert.ok(err)
29 | assert.equal(err.message, 'POST http://guidesmiths-webservice.madgexjbtest.com/billing.asmx failed. Original error was: Test Error')
30 | done()
31 | })
32 | })
33 |
34 | it('should report HTTP failures', function(done) {
35 |
36 | var scope = nock('http://guidesmiths-webservice.madgexjbtest.com')
37 | .post('/billing.asmx')
38 | .reply(503)
39 |
40 | client.billingApi.getCategories(function(err, results) {
41 | assert.ok(err)
42 | assert.equal(err.message, 'POST http://guidesmiths-webservice.madgexjbtest.com/billing.asmx failed. Status code was: 503')
43 | done()
44 | })
45 | })
46 |
47 | it('should report SOAP failures', function(done) {
48 |
49 | var scope = nock('http://guidesmiths-webservice.madgexjbtest.com')
50 | .post('/billing.asmx')
51 | .replyWithFile(400, __dirname + '/replies/soap/SoapFault.xml')
52 |
53 | client.billingApi.getCategories(function(err, results) {
54 | assert.ok(err)
55 | assert.equal(err.message, 'POST http://guidesmiths-webservice.madgexjbtest.com/billing.asmx failed. Status code was: 400')
56 | assert.equal(err.statusCode, 400)
57 | assert.equal(err.faultCode, 'soap:Server')
58 | assert.ok(/System.Web.Services.Protocols.SoapException: Server was unable to process request/.test(err.faultString))
59 | done()
60 | })
61 | })
62 |
63 |
64 |
65 | describe('GetCategories', function() {
66 | it('should get a list of categories', function(done) {
67 |
68 | var scope = nock('http://guidesmiths-webservice.madgexjbtest.com')
69 | .post('/billing.asmx')
70 | .replyWithFile(200, __dirname + '/replies/soap/GetCategories.many.xml')
71 |
72 | client.billingApi.getCategories(function(err, results) {
73 | assert.ifError(err)
74 | assert.equal(results.length, 6)
75 | assert.equal(results[0].mandatory, false)
76 | assert.equal(results[0].multiSelect, true)
77 | assert.equal(results[0].id, 100)
78 | assert.equal(results[0].name, 'Academic Discipline')
79 | done()
80 | })
81 | })
82 |
83 | it('should render single categores as a list', function(done) {
84 |
85 | var scope = nock('http://guidesmiths-webservice.madgexjbtest.com')
86 | .post('/billing.asmx')
87 | .replyWithFile(200, __dirname + '/replies/soap/GetCategories.single.xml')
88 |
89 | client.billingApi.getCategories(function(err, results) {
90 | assert.ifError(err)
91 | assert.equal(results.length, 1)
92 | assert.equal(results[0].mandatory, false)
93 | assert.equal(results[0].multiSelect, true)
94 | assert.equal(results[0].id, 105)
95 | assert.equal(results[0].name, 'Hours')
96 | done()
97 | })
98 | })
99 |
100 | it('should survive bad responses', function(done) {
101 |
102 | var scope = nock('http://guidesmiths-webservice.madgexjbtest.com')
103 | .post('/billing.asmx')
104 | .reply(200, 'not xml mwahahaha')
105 |
106 | client.billingApi.getCategories(function(err, results) {
107 | assert.ok(err)
108 | assert.equal(err.message, 'Non-whitespace before first tag.\nLine: 0\nColumn: 1\nChar: n')
109 | done()
110 | })
111 | })
112 | })
113 |
114 | describe('GetCategoryTerms', function() {
115 |
116 | it('should get a list of category terms', function(done) {
117 |
118 | var scope = nock('http://guidesmiths-webservice.madgexjbtest.com')
119 | .post('/billing.asmx')
120 | .replyWithFile(200, __dirname + '/replies/soap/GetCategoryTerms.many.xml')
121 |
122 | client.billingApi.getCategoryTerms({categoryId: 100}, function(err, results) {
123 | assert.ifError(err)
124 | assert.equal(results.length, 21)
125 | assert.equal(results[0].id, 1)
126 | assert.equal(results[0].name, 'Agriculture, Food and Veterinary')
127 | done()
128 | })
129 | })
130 |
131 |
132 | it('should render single category terms as a list', function(done) {
133 |
134 | var scope = nock('http://guidesmiths-webservice.madgexjbtest.com')
135 | .post('/billing.asmx')
136 | .replyWithFile(200, __dirname + '/replies/soap/GetCategoryTerms.single.xml')
137 |
138 | client.billingApi.getCategoryTerms({categoryId: 100}, function(err, results) {
139 | assert.ifError(err)
140 | assert.equal(results.length, 1)
141 | assert.equal(results[0].id, 21)
142 | assert.equal(results[0].name, 'Sport and Leisure')
143 | done()
144 | })
145 | })
146 | })
147 |
148 | describe('GetLocations', function() {
149 |
150 | it('should get a list of locations', function(done) {
151 |
152 | var scope = nock('http://guidesmiths-webservice.madgexjbtest.com')
153 | .post('/billing.asmx')
154 | .reply(function(uri, requestBody) {
155 | var $ = cheerio.load(requestBody, { xmlMode: true })
156 | assert.equal($('job\\:searchPrefix').text(), 'Lon')
157 | return fs.createReadStream(__dirname + '/replies/soap/GetLocations.many.xml')
158 | })
159 |
160 | client.billingApi.getLocations({prefix: 'Lon'}, function(err, results) {
161 | assert.ifError(err)
162 | assert.equal(results.length, 20)
163 | assert.equal(results[0].locationId, 325)
164 | assert.equal(results[0].label, 'London (Greater)')
165 | done()
166 | })
167 | })
168 |
169 | it('should render single location as a list', function(done) {
170 |
171 | var scope = nock('http://guidesmiths-webservice.madgexjbtest.com')
172 | .post('/billing.asmx')
173 | .replyWithFile(200, __dirname + '/replies/soap/GetLocations.single.xml')
174 |
175 | client.billingApi.getLocations({prefix: 'Longney'}, function(err, results) {
176 | assert.ifError(err)
177 | assert.equal(results.length, 1)
178 | assert.equal(results[0].locationId, 17116)
179 | assert.equal(results[0].label, 'Longney, Gloucester')
180 | done()
181 | })
182 | })
183 | })
184 |
185 | describe('AddBilledJob', function() {
186 |
187 | it('should add a billed job', function(done) {
188 |
189 | var scope = nock('http://guidesmiths-webservice.madgexjbtest.com')
190 | .post('/billing.asmx')
191 | .reply(function(uri, requestBody) {
192 | var $ = cheerio.load(requestBody, { xmlMode: true })
193 | assert.equal($('job\\:sRecruiterBillingID').text(), 'rec-1')
194 | assert.equal($('job\\:sEmployerBillingID').text(), 'emp-1')
195 | assert.equal($('job\\:sJobID').text(), 'job-1')
196 | assert.equal($('job\\:sStartDateTime').text(), '13/01/2015 10:35')
197 | assert.equal($('job\\:sEndDateTime').text(), '14/01/2025 14:55')
198 | assert.equal($('job\\:sApplicationMethod').text(), 'ExternalRedirect')
199 | assert.equal($('job\\:sApplicationEmail').text(), 'foo@example.com')
200 | assert.equal($('job\\:sExternalApplicationURL').text(), 'http://www.example.com/jobzRus')
201 | assert.equal($('job\\:jobProperties job\\:WSJobPropertyValue').length, 2)
202 | assert.equal($('job\\:jobProperties job\\:WSJobPropertyValue:nth-child(1) job\\:Name').text(), 'JobTitle')
203 | assert.equal($('job\\:jobProperties job\\:WSJobPropertyValue:nth-child(1) job\\:Value').text(), 'job-title')
204 | assert.equal($('job\\:jobProperties job\\:WSJobPropertyValue:nth-child(2) job\\:Name').text(), 'JobDescription')
205 | assert.equal($('job\\:jobProperties job\\:WSJobPropertyValue:nth-child(2) job\\:Value').text(), 'job-description')
206 |
207 | assert.equal($('job\\:jobCategorization job\\:WSSelectedTerms').length, 2)
208 | assert.equal($('job\\:jobCategorization job\\:WSSelectedTerms:nth-child(1) job\\:CategoryID').text(), '100')
209 | assert.equal($('job\\:jobCategorization job\\:WSSelectedTerms:nth-child(1) job\\:TermIDs job\\:int').length, 3)
210 | assert.equal($('job\\:jobCategorization job\\:WSSelectedTerms:nth-child(1) job\\:TermIDs job\\:int:nth-child(1)').text(), '1')
211 | assert.equal($('job\\:jobCategorization job\\:WSSelectedTerms:nth-child(1) job\\:TermIDs job\\:int:nth-child(3)').text(), '3')
212 |
213 | assert.equal($('job\\:jobCategorization job\\:WSSelectedTerms:nth-child(2) job\\:CategoryID').text(), '101')
214 | assert.equal($('job\\:jobCategorization job\\:WSSelectedTerms:nth-child(2) job\\:TermIDs job\\:int').length, 3)
215 | assert.equal($('job\\:jobCategorization job\\:WSSelectedTerms:nth-child(2) job\\:TermIDs job\\:int:nth-child(1)').text(), '4')
216 | assert.equal($('job\\:jobCategorization job\\:WSSelectedTerms:nth-child(2) job\\:TermIDs job\\:int:nth-child(3)').text(), '6')
217 |
218 | assert.equal($('job\\:jobUpsells job\\:WSUpsell').length, 2)
219 | assert.equal($('job\\:jobUpsells job\\:WSUpsell:nth-child(1) job\\:ID').text(), '1')
220 | assert.equal($('job\\:jobUpsells job\\:WSUpsell:nth-child(1) job\\:Name').text(), 'Top Jobs')
221 |
222 | assert.equal($('job\\:liProductID').text(), 'product-id')
223 | assert.equal($('job\\:liPriceSoldAt').text(), '100')
224 | assert.equal($('job\\:isBackFill').text(), 'true')
225 | return fs.createReadStream(__dirname + '/replies/soap/AddBilledJob.ok.xml')
226 | })
227 |
228 | client.billingApi.addBilledJob({
229 | id: 'job-1',
230 | recruiterBillingId: 'rec-1',
231 | employerBillingId: 'emp-1',
232 | startDateTime: '13/01/2015 10:35',
233 | endDateTime: '14/01/2025 14:55',
234 | applicationMethod: 'ExternalRedirect',
235 | applicationEmail: 'foo@example.com',
236 | externalApplicationUrl: 'http://www.example.com/jobzRus',
237 | properties: {
238 | JobTitle: 'job-title',
239 | JobDescription: 'job-description'
240 | },
241 | categorization: {
242 | terms: [
243 | {
244 | categoryId: 100,
245 | termIds: [1, 2, 3]
246 | },
247 | {
248 | categoryId: 101,
249 | termIds: [4, 5, 6]
250 | }
251 | ]
252 | },
253 | upsells: [
254 | {
255 | id: '1',
256 | name: 'Top Jobs'
257 | },
258 | {
259 | id: '2'
260 | }
261 | ],
262 | productId: 'product-id',
263 | priceSoldAt: 100,
264 | isBackFill: true
265 | }, function(err, result) {
266 | assert.ifError(err)
267 | assert.equal(result, 1340)
268 | done()
269 | })
270 | })
271 |
272 | it('should omit optional elements when not specified', function(done) {
273 |
274 | var scope = nock('http://guidesmiths-webservice.madgexjbtest.com')
275 | .post('/billing.asmx')
276 | .reply(function(uri, requestBody) {
277 | var $ = cheerio.load(requestBody, { xmlMode: true })
278 | assert.equal($('job\\:sStartDateTime').length, 0)
279 | assert.equal($('job\\:sEndDateTime').length, 0)
280 | assert.equal($('job\\:sApplicationMethod').length, 0)
281 | assert.equal($('job\\:sApplicationEmail').length, 0)
282 | assert.equal($('job\\:sExternalApplicationURL').length, 0)
283 | assert.equal($('job\\:jobProperties').length, 1)
284 | assert.equal($('job\\:jobProperties job\\:WSJobPropertyValue').length, 0)
285 | assert.equal($('job\\:jobCategorization').length, 1)
286 | assert.equal($('job\\:jobCategorization job\\:WSSelectedTerms').length, 0)
287 | assert.equal($('job\\:jobUpsells').length, 0)
288 | return fs.createReadStream(__dirname + '/replies/soap/AddBilledJob.ok.xml')
289 | })
290 |
291 | client.billingApi.addBilledJob({
292 | id: 'job-1',
293 | recruiterBillingId: 'rec-1',
294 | employerBillingId: 'emp-1'
295 | }, function(err, results) {
296 | assert.ifError(err)
297 | done()
298 | })
299 | })
300 |
301 | it('should use xsi:nil for certain elements with minOcccurs=1 mandatory=false when not specified', function(done) {
302 |
303 | var scope = nock('http://guidesmiths-webservice.madgexjbtest.com')
304 | .post('/billing.asmx')
305 | .reply(function(uri, requestBody) {
306 | var $ = cheerio.load(requestBody, { xmlMode: true })
307 | assert.equal($('job\\:liProductID').length, 1)
308 | assert.equal($('job\\:liProductID').attr('xsi:nil'), 'true')
309 | assert.equal($('job\\:liPriceSoldAt').length, 1)
310 | assert.equal($('job\\:liPriceSoldAt').attr('xsi:nil'), 'true')
311 | assert.equal($('job\\:isBackFill').length, 1)
312 | assert.equal($('job\\:isBackFill').attr('xsi:nil'), 'true')
313 | return fs.createReadStream(__dirname + '/replies/soap/AddBilledJob.ok.xml')
314 | })
315 |
316 | client.billingApi.addBilledJob({
317 | id: 'job-1',
318 | recruiterBillingId: 'rec-1',
319 | employerBillingId: 'emp-1'
320 | }, function(err, results) {
321 | assert.ifError(err)
322 | done()
323 | })
324 | })
325 | })
326 |
327 | describe('UpdateBilledJob', function() {
328 |
329 | it('should update a billed job', function(done) {
330 | var scope = nock('http://guidesmiths-webservice.madgexjbtest.com')
331 | .post('/billing.asmx')
332 | .reply(function(uri, requestBody) {
333 | var $ = cheerio.load(requestBody, { xmlMode: true })
334 | assert.equal($('job\\:sRecruiterBillingID').text(), 'rec-1')
335 | assert.equal($('job\\:sEmployerBillingID').text(), 'emp-1')
336 | assert.equal($('job\\:sJobID').text(), 'job-1')
337 | return fs.createReadStream(__dirname + '/replies/soap/UpdateBilledJob.ok.xml')
338 | })
339 |
340 | client.billingApi.updateBilledJob({
341 | id: 'job-1',
342 | recruiterBillingId: 'rec-1',
343 | employerBillingId: 'emp-1',
344 | startDateTime: '13/01/2015 10:35',
345 | endDateTime: '14/01/2025 14:55',
346 | applicationMethod: 'ExternalRedirect',
347 | applicationEmail: 'foo@example.com',
348 | externalApplicationUrl: 'http://www.example.com/jobzRus',
349 | properties: {
350 | JobTitle: 'job-title',
351 | JobDescription: 'job-description'
352 | },
353 | categorization: {
354 | terms: [
355 | {
356 | categoryId: 100,
357 | termIds: [1, 2, 3]
358 | },
359 | {
360 | categoryId: 101,
361 | termIds: [4, 5, 6]
362 | }
363 | ]
364 | },
365 | productId: 'product-id',
366 | priceSoldAt: 100,
367 | isBackFill: true
368 | }, function(err, result) {
369 | assert.ifError(err)
370 | assert.equal(result, 1340)
371 | done()
372 | })
373 | })
374 | })
375 |
376 |
377 | describe('AddRecruiterV2', function() {
378 |
379 | it('should add recruiter', function(done) {
380 |
381 | var scope = nock('http://guidesmiths-webservice.madgexjbtest.com')
382 | .post('/billing.asmx')
383 | .reply(function(uri, requestBody) {
384 | var $ = cheerio.load(requestBody, { xmlMode: true })
385 | assert.equal($('job\\:RecruiterName').text(), 'recruiter-name')
386 | assert.equal($('job\\:CustomerBillingID').text(), 'cust-billing-id')
387 | assert.equal($('job\\:ContactFirstName').text(), 'contact-fn')
388 | assert.equal($('job\\:ContactLastName').text(), 'contact-ln-1')
389 | assert.equal($('job\\:ContactLastName2').text(), 'contact-ln-2')
390 | assert.equal($('job\\:Address1').text(), 'address-1')
391 | assert.equal($('job\\:Address2').text(), 'address-2')
392 | assert.equal($('job\\:Address3').text(), 'address-3')
393 | assert.equal($('job\\:TownCity').text(), 'town')
394 | assert.equal($('job\\:CountyState').text(), 'county')
395 | assert.equal($('job\\:ZipPostCode').text(), 'zip')
396 | assert.equal($('job\\:Country').text(), 'country')
397 | assert.equal($('job\\:Telephone').text(), 'phone')
398 | assert.equal($('job\\:ContactEmail').text(), 'contact@example.com')
399 | assert.equal($('job\\:RecruiterTypeID').text(), 1)
400 | assert.equal($('job\\:Image job\\:AssetID').text(), 'image-asset-id')
401 | assert.equal($('job\\:Image job\\:Base64BlobString').text(), 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP4z8BQDwAEgAF/posBPQAAAABJRU5ErkJggg==')
402 | assert.equal($('job\\:EnhancedJobHtml job\\:JobTitleTextCss').text(), '#000')
403 | assert.equal($('job\\:EnhancedJobHtml job\\:JobHeadingsTextCss').text(), '#111')
404 | assert.equal($('job\\:EnhancedJobHtml job\\:ButtonBackgroundCss').text(), '#222')
405 | assert.equal($('job\\:EnhancedJobHtml job\\:ButtonTextCss').text(), '#333')
406 | assert.equal($('job\\:EnhancedJobHtml job\\:HyperLinkCss').text(), '#444')
407 | assert.equal($('job\\:EnhancedJobHtml job\\:Banner job\\:AssetID').text(), 'banner-asset-id')
408 | assert.equal($('job\\:EnhancedJobHtml job\\:Banner job\\:Base64BlobString').text(), 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP4z8BQDwAEgAF/posBPQAAAABJRU5ErkJggg==')
409 | assert.equal($('job\\:AccountManager job\\:EmailAddress').text(), 'manager@example.com')
410 | assert.equal($('job\\:AccountManager job\\:FirstName').text(), 'manager-fn')
411 | assert.equal($('job\\:AccountManager job\\:LastName').text(), 'manager-ln')
412 | assert.equal($('job\\:CanAccessCvDatabase').text(), 'true')
413 |
414 | return fs.createReadStream(__dirname + '/replies/soap/AddRecruiterV2.ok.xml')
415 | })
416 |
417 | client.billingApi.addRecruiterV2({
418 | recruiterName: 'recruiter-name',
419 | customerBillingID: 'cust-billing-id',
420 | contactFirstName: 'contact-fn',
421 | contactLastName: 'contact-ln-1',
422 | contactLastName2: 'contact-ln-2',
423 | address1: 'address-1',
424 | address2: 'address-2',
425 | address3: 'address-3',
426 | townCity: 'town',
427 | countyState: 'county',
428 | zipPostCode: 'zip',
429 | country: 'country',
430 | telephone: 'phone',
431 | contactEmail: 'contact@example.com',
432 | recruiterTypeID: 1,
433 | image: {
434 | assetId: 'image-asset-id',
435 | base64BlobString: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP4z8BQDwAEgAF/posBPQAAAABJRU5ErkJggg=='
436 | },
437 | enhancedJobHtml: {
438 | jobTitleTextCss: '#000',
439 | jobHeadingsTextCss: '#111',
440 | buttonBackgroundCss: '#222',
441 | buttonTextCss: '#333',
442 | hyperLinkCss: '#444',
443 | banner: {
444 | assetId: 'banner-asset-id',
445 | base64BlobString: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP4z8BQDwAEgAF/posBPQAAAABJRU5ErkJggg=='
446 | }
447 | },
448 | accountManager: {
449 | emailAddress: 'manager@example.com',
450 | firstName: 'manager-fn',
451 | lastName: 'manager-ln',
452 | },
453 | canAccessCvDatabase: true
454 | }, function(err, result) {
455 | assert.ifError(err)
456 | assert.equal(result, 1339)
457 | done()
458 | })
459 | })
460 | })
461 |
462 | describe('UpdateRecruiterWithBillingID', function() {
463 |
464 | it('should update recruiter', function(done) {
465 |
466 | var scope = nock('http://guidesmiths-webservice.madgexjbtest.com')
467 | .post('/billing.asmx')
468 | .reply(function(uri, requestBody) {
469 | var $ = cheerio.load(requestBody, { xmlMode: true })
470 | assert.equal($('job\\:RecruiterName').text(), 'recruiter-name')
471 | assert.equal($('job\\:CustomerBillingID').text(), 'cust-billing-id')
472 | assert.equal($('job\\:ContactFirstName').text(), 'contact-fn')
473 | assert.equal($('job\\:ContactLastName').text(), 'contact-ln-1')
474 | assert.equal($('job\\:ContactLastName2').text(), 'contact-ln-2')
475 | assert.equal($('job\\:Address1').text(), 'address-1')
476 | assert.equal($('job\\:Address2').text(), 'address-2')
477 | assert.equal($('job\\:Address3').text(), 'address-3')
478 | assert.equal($('job\\:TownCity').text(), 'town')
479 | assert.equal($('job\\:CountyState').text(), 'county')
480 | assert.equal($('job\\:ZipPostCode').text(), 'zip')
481 | assert.equal($('job\\:Country').text(), 'country')
482 | assert.equal($('job\\:Telephone').text(), 'phone')
483 | assert.equal($('job\\:ContactEmail').text(), 'contact@example.com')
484 | assert.equal($('job\\:RecruiterTypeID').text(), 1)
485 | assert.equal($('job\\:Image job\\:AssetID').text(), 'image-asset-id')
486 | assert.equal($('job\\:Image job\\:Base64BlobString').text(), 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP4z8BQDwAEgAF/posBPQAAAABJRU5ErkJggg==')
487 | assert.equal($('job\\:EnhancedJobHtml job\\:JobTitleTextCss').text(), '#000')
488 | assert.equal($('job\\:EnhancedJobHtml job\\:JobHeadingsTextCss').text(), '#111')
489 | assert.equal($('job\\:EnhancedJobHtml job\\:ButtonBackgroundCss').text(), '#222')
490 | assert.equal($('job\\:EnhancedJobHtml job\\:ButtonTextCss').text(), '#333')
491 | assert.equal($('job\\:EnhancedJobHtml job\\:HyperLinkCss').text(), '#444')
492 | assert.equal($('job\\:EnhancedJobHtml job\\:Banner job\\:AssetID').text(), 'banner-asset-id')
493 | assert.equal($('job\\:EnhancedJobHtml job\\:Banner job\\:Base64BlobString').text(), 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP4z8BQDwAEgAF/posBPQAAAABJRU5ErkJggg==')
494 | assert.equal($('job\\:AccountManager job\\:EmailAddress').text(), 'manager@example.com')
495 | assert.equal($('job\\:AccountManager job\\:FirstName').text(), 'manager-fn')
496 | assert.equal($('job\\:AccountManager job\\:LastName').text(), 'manager-ln')
497 | assert.equal($('job\\:CanAccessCvDatabase').text(), 'true')
498 |
499 | return fs.createReadStream(__dirname + '/replies/soap/UpdateRecruiterWithBillingID.ok.xml')
500 | })
501 |
502 | client.billingApi.updateRecruiterWithBillingId({
503 | recruiterName: 'recruiter-name',
504 | customerBillingID: 'cust-billing-id',
505 | contactFirstName: 'contact-fn',
506 | contactLastName: 'contact-ln-1',
507 | contactLastName2: 'contact-ln-2',
508 | address1: 'address-1',
509 | address2: 'address-2',
510 | address3: 'address-3',
511 | townCity: 'town',
512 | countyState: 'county',
513 | zipPostCode: 'zip',
514 | country: 'country',
515 | telephone: 'phone',
516 | contactEmail: 'contact@example.com',
517 | recruiterTypeID: 1,
518 | image: {
519 | assetId: 'image-asset-id',
520 | base64BlobString: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP4z8BQDwAEgAF/posBPQAAAABJRU5ErkJggg=='
521 | },
522 | enhancedJobHtml: {
523 | jobTitleTextCss: '#000',
524 | jobHeadingsTextCss: '#111',
525 | buttonBackgroundCss: '#222',
526 | buttonTextCss: '#333',
527 | hyperLinkCss: '#444',
528 | banner: {
529 | assetId: 'banner-asset-id',
530 | base64BlobString: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP4z8BQDwAEgAF/posBPQAAAABJRU5ErkJggg=='
531 | }
532 | },
533 | accountManager: {
534 | emailAddress: 'manager@example.com',
535 | firstName: 'manager-fn',
536 | lastName: 'manager-ln',
537 | },
538 | canAccessCvDatabase: true
539 | }, function(err, result) {
540 | assert.ifError(err)
541 | assert.equal(result, 1339)
542 | done()
543 | })
544 | })
545 | })
546 |
547 | describe('CheckRecruiterExistsV2', function() {
548 |
549 | it('should find a Recruiter by madgex id', function(done) {
550 |
551 | var scope = nock('http://guidesmiths-webservice.madgexjbtest.com')
552 | .post('/billing.asmx')
553 | .replyWithFile(200, __dirname + '/replies/soap/CheckRecruiterExistsV2.single.xml')
554 |
555 | client.billingApi.checkRecruiterExistsV2({sRecruiterName: '', sCustomerBillingId: '', sMadgexID: '11713'}, function(err, results) {
556 | assert.ifError(err)
557 | assert.equal(results.exists, true)
558 | assert.equal(results.billingId, 23005869)
559 | assert.equal(results.id, 11713)
560 | done()
561 | })
562 | })
563 | })
564 |
565 | describe('CheckRecruiterExistsV2', function() {
566 |
567 | it('a Recruiter by customer billing id', function(done) {
568 |
569 | var scope = nock('http://guidesmiths-webservice.madgexjbtest.com')
570 | .post('/billing.asmx')
571 | .replyWithFile(200, __dirname + '/replies/soap/CheckRecruiterExistsV2.single.xml')
572 |
573 | client.billingApi.checkRecruiterExistsV2({sRecruiterName: '', sCustomerBillingId: '23005869', sMadgexID: ''}, function(err, results) {
574 | assert.ifError(err)
575 | assert.equal(results.exists, true)
576 | assert.equal(results.billingId, 23005869)
577 | assert.equal(results.id, 11713)
578 | done()
579 | })
580 | })
581 | })
582 |
583 | describe('CheckRecruiterExistsV2 with promises', function() {
584 |
585 | it('resolve with promise - a Recruiter by customer billing id', function(done) {
586 |
587 | var scope = nock('http://guidesmiths-webservice.madgexjbtest.com')
588 | .post('/billing.asmx')
589 | .replyWithFile(200, __dirname + '/replies/soap/CheckRecruiterExistsV2.single.xml')
590 |
591 | client.billingApi.checkRecruiterExistsV2({sRecruiterName: '', sCustomerBillingId: '23005869', sMadgexID: ''})
592 | .then(function (results) {
593 | assert.equal(results.exists, true)
594 | assert.equal(results.billingId, 23005869)
595 | assert.equal(results.id, 11713)
596 | done()
597 | }, function (err) {
598 | assert.ifError(err)
599 | })
600 | })
601 | })
602 | })
603 | })
604 |
605 |
606 |
--------------------------------------------------------------------------------
/test/replies/rest/get.jobinfo.search.json:
--------------------------------------------------------------------------------
1 | {
2 | "status": "ok",
3 | "result": {
4 | "uriTemplate": "http:\/\/guidesmiths-web.madgexjbtest.com\/job\/{jobId}\/?trackid=22",
5 | "totalResults": 132,
6 | "startIndex": 0,
7 | "itemsPerPage": 30,
8 | "jobs": [
9 | {
10 | "id": 1131,
11 | "title": "Inbound Sales Advisor",
12 | "posted": "21 Apr 2015",
13 | "description": "",
14 | "recruiter": "Transport for London (Madgex Test Data)",
15 | "recruiterID": 21,
16 | "recruiterLogo": "8bc4bed9-0fc3-42b3-a3a0-920e195f8ca4",
17 | "salary": "\u00a330,001 - \u00a340,000",
18 | "location": "ME2 4JD, Rochester",
19 | "geo": [
20 |
21 | ],
22 | "jobRole": null,
23 | "applyInfo": {
24 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1131\/inbound-sales-advisor\/?trackid=22",
25 | "type": "email-applications",
26 | "allowMobileExternalRedirects": true
27 | },
28 | "upsells": [
29 |
30 | ],
31 | "noOfVideos": 0,
32 | "isPremiumJob": false,
33 | "isHomePageJob": false,
34 | "isDisplayLogoJob": false,
35 | "isTopJob": false,
36 | "categories": {
37 | "function": [
38 | "Academic Related",
39 | [
40 | "Professional Services",
41 | [
42 | "Administrative",
43 | "Finance and Procurement"
44 | ]
45 | ]
46 | ],
47 | "location": [
48 |
49 | ]
50 | },
51 | "urlId": "1131\/inbound-sales-advisor",
52 | "usesAltRecruiterName": false
53 | },
54 | {
55 | "id": 1124,
56 | "title": "Vice Chancellor",
57 | "posted": "21 Apr 2015",
58 | "description": "",
59 | "recruiter": "Kings College London",
60 | "recruiterID": 1117,
61 | "recruiterLogo": "260374d2-32db-40b4-a7c7-959bf52a03f9",
62 | "salary": "Competitive",
63 | "location": "London (Central), London (Greater)",
64 | "geo": [
65 |
66 | ],
67 | "jobRole": null,
68 | "applyInfo": {
69 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1124\/vice-chancellor\/?trackid=22",
70 | "type": "response-management",
71 | "allowMobileExternalRedirects": true,
72 | "externalApplicationUrl": "http:\/\/www.kcl.ac.uk\/hr\/jobs\/principal.aspx"
73 | },
74 | "upsells": [
75 | 5,
76 | 6,
77 | 7
78 | ],
79 | "noOfVideos": 0,
80 | "isPremiumJob": true,
81 | "isHomePageJob": false,
82 | "isDisplayLogoJob": true,
83 | "isTopJob": false,
84 | "categories": {
85 | "function": [
86 | [
87 | "Senior Management & Heads of Department",
88 | [
89 | "Vice-chancellors \/ Principals \/ Provosts",
90 | "Deputy Vice-chancellors",
91 | "Pro Vice-chancellors"
92 | ]
93 | ]
94 | ],
95 | "location": [
96 |
97 | ]
98 | },
99 | "urlId": "1124\/vice-chancellor",
100 | "usesAltRecruiterName": false
101 | },
102 | {
103 | "id": 1123,
104 | "title": "Lecturer in Computer Science",
105 | "posted": "21 Apr 2015",
106 | "description": "",
107 | "recruiter": "Kings College London",
108 | "recruiterID": 1117,
109 | "recruiterLogo": "260374d2-32db-40b4-a7c7-959bf52a03f9",
110 | "salary": "\u00a335,000 - \u00a340,000",
111 | "location": "London (Central), London (Greater)",
112 | "geo": [
113 |
114 | ],
115 | "jobRole": null,
116 | "applyInfo": {
117 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1123\/lecturer-in-computer-science\/?trackid=22",
118 | "type": "response-management",
119 | "allowMobileExternalRedirects": true,
120 | "externalApplicationUrl": "http:\/\/www.kcl.ac.uk\/hr\/jobs\/principal.aspx"
121 | },
122 | "upsells": [
123 |
124 | ],
125 | "noOfVideos": 0,
126 | "isPremiumJob": false,
127 | "isHomePageJob": false,
128 | "isDisplayLogoJob": true,
129 | "isTopJob": false,
130 | "categories": {
131 | "function": [
132 | [
133 | "Academic Posts",
134 | [
135 | "Lecturers \/ Assistant Professors"
136 | ]
137 | ]
138 | ],
139 | "location": [
140 |
141 | ]
142 | },
143 | "urlId": "1123\/lecturer-in-computer-science",
144 | "usesAltRecruiterName": false
145 | },
146 | {
147 | "id": 1122,
148 | "title": "Head of Humantities",
149 | "posted": "21 Apr 2015",
150 | "description": "",
151 | "recruiter": "Kings College London",
152 | "recruiterID": 1117,
153 | "recruiterLogo": "260374d2-32db-40b4-a7c7-959bf52a03f9",
154 | "salary": "\u00a380,000 - \u00a380,001",
155 | "location": "London (Central), London (Greater)",
156 | "geo": [
157 |
158 | ],
159 | "jobRole": null,
160 | "applyInfo": {
161 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1122\/head-of-humantities\/?trackid=22",
162 | "type": "response-management",
163 | "allowMobileExternalRedirects": true,
164 | "externalApplicationUrl": "http:\/\/www.kcl.ac.uk\/hr\/jobs\/principal.aspx"
165 | },
166 | "upsells": [
167 | 5
168 | ],
169 | "noOfVideos": 0,
170 | "isPremiumJob": true,
171 | "isHomePageJob": false,
172 | "isDisplayLogoJob": true,
173 | "isTopJob": false,
174 | "categories": {
175 | "function": [
176 | [
177 | "Senior Management & Heads of Department",
178 | [
179 | "Heads of Department"
180 | ]
181 | ]
182 | ],
183 | "location": [
184 |
185 | ]
186 | },
187 | "urlId": "1122\/head-of-humantities",
188 | "usesAltRecruiterName": false
189 | },
190 | {
191 | "id": 1261,
192 | "title": "Senior Lecturer Appointment ",
193 | "posted": "21 Apr 2015",
194 | "description": "",
195 | "recruiter": "The British University in Egypt",
196 | "recruiterID": 1258,
197 | "recruiterLogo": "492dc557-44f7-46c8-83f8-5ba20eaf9f2a",
198 | "salary": "",
199 | "location": "Egypt (EG)",
200 | "geo": [
201 |
202 | ],
203 | "jobRole": null,
204 | "applyInfo": {
205 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1261\/senior-lecturer-appointment-\/?trackid=22",
206 | "type": "email-applications",
207 | "allowMobileExternalRedirects": true
208 | },
209 | "upsells": [
210 | 6
211 | ],
212 | "noOfVideos": 0,
213 | "isPremiumJob": false,
214 | "isHomePageJob": false,
215 | "isDisplayLogoJob": true,
216 | "isTopJob": false,
217 | "categories": {
218 | "function": [
219 | [
220 | "Academic Posts",
221 | [
222 | "Principal \/ Senior Lecturers \/ Associate Professors"
223 | ]
224 | ]
225 | ],
226 | "location": [
227 |
228 | ]
229 | },
230 | "urlId": "1261\/senior-lecturer-appointment-",
231 | "usesAltRecruiterName": false
232 | },
233 | {
234 | "id": 1237,
235 | "title": "Academic Registrar",
236 | "posted": "21 Apr 2015",
237 | "description": "",
238 | "recruiter": "London School of Economic & Political Science",
239 | "recruiterID": 1236,
240 | "recruiterLogo": "4a566a38-2cd6-43ad-97e0-08733cd04c13",
241 | "salary": "",
242 | "location": "London (Greater)",
243 | "geo": [
244 |
245 | ],
246 | "jobRole": null,
247 | "applyInfo": {
248 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1237\/academic-registrar\/?trackid=22",
249 | "type": "email-applications",
250 | "allowMobileExternalRedirects": true
251 | },
252 | "upsells": [
253 | 6,
254 | 5
255 | ],
256 | "noOfVideos": 0,
257 | "isPremiumJob": false,
258 | "isHomePageJob": false,
259 | "isDisplayLogoJob": true,
260 | "isTopJob": false,
261 | "categories": {
262 | "function": [
263 | [
264 | "Senior Management & Heads of Department",
265 | [
266 | "Registrars \/ Assistant Registrars"
267 | ]
268 | ]
269 | ],
270 | "location": [
271 |
272 | ]
273 | },
274 | "urlId": "1237\/academic-registrar",
275 | "usesAltRecruiterName": false
276 | },
277 | {
278 | "id": 1235,
279 | "title": "Vice-Chancellor",
280 | "posted": "21 Apr 2015",
281 | "description": "",
282 | "recruiter": "University of Warwick",
283 | "recruiterID": 1234,
284 | "recruiterLogo": "336b72c0-20f3-4961-90fe-ea706ff7215c",
285 | "salary": "",
286 | "location": "Warwick, Warwickshire",
287 | "geo": [
288 |
289 | ],
290 | "jobRole": null,
291 | "applyInfo": {
292 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1235\/vice-chancellor\/?trackid=22",
293 | "type": "external-redirect",
294 | "allowMobileExternalRedirects": true,
295 | "externalApplicationUrl": "http:\/\/www.perrettlaver.com\/candidates"
296 | },
297 | "upsells": [
298 | 5,
299 | 6
300 | ],
301 | "noOfVideos": 0,
302 | "isPremiumJob": false,
303 | "isHomePageJob": false,
304 | "isDisplayLogoJob": true,
305 | "isTopJob": false,
306 | "categories": {
307 | "function": [
308 | [
309 | "Senior Management & Heads of Department",
310 | [
311 | "Vice-chancellors \/ Principals \/ Provosts"
312 | ]
313 | ]
314 | ],
315 | "location": [
316 |
317 | ]
318 | },
319 | "urlId": "1235\/vice-chancellor",
320 | "usesAltRecruiterName": false
321 | },
322 | {
323 | "id": 1233,
324 | "title": "Lecturer\/Senior Lecturer Posts in Disruptive Technologies",
325 | "posted": "21 Apr 2015",
326 | "description": "",
327 | "recruiter": "University of Greenwich",
328 | "recruiterID": 1229,
329 | "recruiterLogo": "69beea65-72a0-4dde-b5e2-0122b9e4532c",
330 | "salary": "\u00a331,342 to \u00a345,954 plus \u00a33,437 London weighting per annum",
331 | "location": "London (Greater)",
332 | "geo": [
333 |
334 | ],
335 | "jobRole": null,
336 | "applyInfo": {
337 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1233\/lecturer-senior-lecturer-posts-in-disruptive-technologies\/?trackid=22",
338 | "type": "external-redirect",
339 | "allowMobileExternalRedirects": true,
340 | "externalApplicationUrl": "https:\/\/jobs.gre.ac.uk\/Vacancy.aspx?ref=840"
341 | },
342 | "upsells": [
343 | 8,
344 | 5,
345 | 7,
346 | 6
347 | ],
348 | "noOfVideos": 0,
349 | "isPremiumJob": false,
350 | "isHomePageJob": false,
351 | "isDisplayLogoJob": true,
352 | "isTopJob": false,
353 | "categories": {
354 | "function": [
355 | [
356 | "Senior Management & Heads of Department",
357 | [
358 | "Heads of Department",
359 | "Other Senior Management"
360 | ]
361 | ]
362 | ],
363 | "location": [
364 |
365 | ]
366 | },
367 | "urlId": "1233\/lecturer-senior-lecturer-posts-in-disruptive-technologies",
368 | "usesAltRecruiterName": false
369 | },
370 | {
371 | "id": 1232,
372 | "title": "Subject Lead for Film and Television",
373 | "posted": "21 Apr 2015",
374 | "description": "",
375 | "recruiter": "University of Greenwich",
376 | "recruiterID": 1229,
377 | "recruiterLogo": "69beea65-72a0-4dde-b5e2-0122b9e4532c",
378 | "salary": "\u00a347,328 to \u00a354,841 plus \u00a33,437 London weighting per annum",
379 | "location": "London (Greater)",
380 | "geo": [
381 |
382 | ],
383 | "jobRole": null,
384 | "applyInfo": {
385 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1232\/subject-lead-for-film-and-television\/?trackid=22",
386 | "type": "external-redirect",
387 | "allowMobileExternalRedirects": true,
388 | "externalApplicationUrl": "https:\/\/jobs.gre.ac.uk\/Vacancy.aspx?ref=841"
389 | },
390 | "upsells": [
391 | 8,
392 | 5,
393 | 7,
394 | 6
395 | ],
396 | "noOfVideos": 0,
397 | "isPremiumJob": false,
398 | "isHomePageJob": false,
399 | "isDisplayLogoJob": true,
400 | "isTopJob": false,
401 | "categories": {
402 | "function": [
403 | [
404 | "Senior Management & Heads of Department",
405 | [
406 | "Heads of Department",
407 | "Other Senior Management"
408 | ]
409 | ]
410 | ],
411 | "location": [
412 |
413 | ]
414 | },
415 | "urlId": "1232\/subject-lead-for-film-and-television",
416 | "usesAltRecruiterName": false
417 | },
418 | {
419 | "id": 1263,
420 | "title": "Deputy Vice-Chancellor - Student Experience",
421 | "posted": "21 Apr 2015",
422 | "description": "",
423 | "recruiter": "University of Westminster",
424 | "recruiterID": 1262,
425 | "recruiterLogo": "f2cb4e60-d376-4655-b756-6a2d3502d620",
426 | "salary": "Competitive salary",
427 | "location": "London (Greater)",
428 | "geo": [
429 |
430 | ],
431 | "jobRole": null,
432 | "applyInfo": {
433 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1263\/deputy-vice-chancellor-student-experience\/?trackid=22",
434 | "type": "external-redirect",
435 | "allowMobileExternalRedirects": true,
436 | "externalApplicationUrl": "http:\/\/www.perrettlaver.com\/westminster"
437 | },
438 | "upsells": [
439 | 6
440 | ],
441 | "noOfVideos": 0,
442 | "isPremiumJob": false,
443 | "isHomePageJob": false,
444 | "isDisplayLogoJob": true,
445 | "isTopJob": false,
446 | "categories": {
447 | "function": [
448 | [
449 | "Senior Management & Heads of Department",
450 | [
451 | "Deputy Vice-chancellors"
452 | ]
453 | ]
454 | ],
455 | "location": [
456 |
457 | ]
458 | },
459 | "urlId": "1263\/deputy-vice-chancellor-student-experience",
460 | "usesAltRecruiterName": false
461 | },
462 | {
463 | "id": 1231,
464 | "title": "Deputy Head of Department, Creative Professions and Digital Arts",
465 | "posted": "21 Apr 2015",
466 | "description": "",
467 | "recruiter": "University of Greenwich",
468 | "recruiterID": 1229,
469 | "recruiterLogo": "69beea65-72a0-4dde-b5e2-0122b9e4532c",
470 | "salary": "\u00a347,328 to \u00a354,841 plus \u00a33,437 London weighting per annum",
471 | "location": "London (Greater)",
472 | "geo": [
473 |
474 | ],
475 | "jobRole": null,
476 | "applyInfo": {
477 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1231\/deputy-head-of-department-creative-professions-and-digital-arts\/?trackid=22",
478 | "type": "external-redirect",
479 | "allowMobileExternalRedirects": true,
480 | "externalApplicationUrl": "https:\/\/jobs.gre.ac.uk\/Vacancy.aspx?ref=840"
481 | },
482 | "upsells": [
483 | 8,
484 | 5,
485 | 7,
486 | 6
487 | ],
488 | "noOfVideos": 0,
489 | "isPremiumJob": false,
490 | "isHomePageJob": false,
491 | "isDisplayLogoJob": true,
492 | "isTopJob": false,
493 | "categories": {
494 | "function": [
495 | [
496 | "Senior Management & Heads of Department",
497 | [
498 | "Heads of Department",
499 | "Other Senior Management"
500 | ]
501 | ]
502 | ],
503 | "location": [
504 |
505 | ]
506 | },
507 | "urlId": "1231\/deputy-head-of-department-creative-professions-and-digital-arts",
508 | "usesAltRecruiterName": false
509 | },
510 | {
511 | "id": 1297,
512 | "title": "Quantity Surveyor",
513 | "posted": "16 Apr 2015",
514 | "description": "",
515 | "recruiter": "Transport for London (Madgex Test Data)",
516 | "recruiterID": 21,
517 | "recruiterLogo": "8bc4bed9-0fc3-42b3-a3a0-920e195f8ca4",
518 | "salary": "\u00a3180,001 - \u00a3190,000",
519 | "location": "B34 7JL, Birmingham",
520 | "geo": [
521 |
522 | ],
523 | "jobRole": null,
524 | "applyInfo": {
525 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1297\/quantity-surveyor\/?trackid=22",
526 | "type": "email-applications",
527 | "allowMobileExternalRedirects": true
528 | },
529 | "upsells": [
530 |
531 | ],
532 | "noOfVideos": 0,
533 | "isPremiumJob": false,
534 | "isHomePageJob": false,
535 | "isDisplayLogoJob": false,
536 | "isTopJob": false,
537 | "categories": {
538 | "function": [
539 | [
540 | "Academic Related",
541 | [
542 | "Technicians"
543 | ]
544 | ]
545 | ],
546 | "location": [
547 |
548 | ]
549 | },
550 | "urlId": "1297\/quantity-surveyor",
551 | "usesAltRecruiterName": false
552 | },
553 | {
554 | "id": 1299,
555 | "title": "Strategic Procurement Manager",
556 | "posted": "16 Apr 2015",
557 | "description": "",
558 | "recruiter": "Transport for London (Madgex Test Data)",
559 | "recruiterID": 21,
560 | "recruiterLogo": "8bc4bed9-0fc3-42b3-a3a0-920e195f8ca4",
561 | "salary": "\u00a3120,001 - \u00a3130,000",
562 | "location": "CM77 8NJ, Braintree",
563 | "geo": [
564 |
565 | ],
566 | "jobRole": null,
567 | "applyInfo": {
568 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1299\/strategic-procurement-manager\/?trackid=22",
569 | "type": "email-applications",
570 | "allowMobileExternalRedirects": true
571 | },
572 | "upsells": [
573 | 5
574 | ],
575 | "noOfVideos": 0,
576 | "isPremiumJob": true,
577 | "isHomePageJob": false,
578 | "isDisplayLogoJob": false,
579 | "isTopJob": false,
580 | "categories": {
581 | "function": [
582 | [
583 | "Professional Services",
584 | [
585 | "Human Resources"
586 | ]
587 | ]
588 | ],
589 | "location": [
590 |
591 | ]
592 | },
593 | "urlId": "1299\/strategic-procurement-manager",
594 | "usesAltRecruiterName": false
595 | },
596 | {
597 | "id": 1298,
598 | "title": "Trust Fundraiser",
599 | "posted": "16 Apr 2015",
600 | "description": "",
601 | "recruiter": "Transport for London (Madgex Test Data)",
602 | "recruiterID": 21,
603 | "recruiterLogo": "8bc4bed9-0fc3-42b3-a3a0-920e195f8ca4",
604 | "salary": "Hourly Rate",
605 | "location": "SG1 6AB, Stevenage",
606 | "geo": [
607 |
608 | ],
609 | "jobRole": null,
610 | "applyInfo": {
611 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1298\/trust-fundraiser\/?trackid=22",
612 | "type": "email-applications",
613 | "allowMobileExternalRedirects": true
614 | },
615 | "upsells": [
616 |
617 | ],
618 | "noOfVideos": 0,
619 | "isPremiumJob": false,
620 | "isHomePageJob": false,
621 | "isDisplayLogoJob": false,
622 | "isTopJob": false,
623 | "categories": {
624 | "function": [
625 | [
626 | "Senior Management & Heads of Department",
627 | [
628 | "Vice-chancellors \/ Principals \/ Provosts"
629 | ]
630 | ]
631 | ],
632 | "location": [
633 |
634 | ]
635 | },
636 | "urlId": "1298\/trust-fundraiser",
637 | "usesAltRecruiterName": false
638 | },
639 | {
640 | "id": 1300,
641 | "title": "Business Travel Reservations",
642 | "posted": "16 Apr 2015",
643 | "description": "",
644 | "recruiter": "Transport for London (Madgex Test Data)",
645 | "recruiterID": 21,
646 | "recruiterLogo": "8bc4bed9-0fc3-42b3-a3a0-920e195f8ca4",
647 | "salary": "\u00a3110,001 - \u00a3120,000",
648 | "location": "S65 4DA, Rotherham",
649 | "geo": [
650 |
651 | ],
652 | "jobRole": null,
653 | "applyInfo": {
654 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1300\/business-travel-reservations\/?trackid=22",
655 | "type": "response-management",
656 | "allowMobileExternalRedirects": true
657 | },
658 | "upsells": [
659 | 5,
660 | 6,
661 | 7
662 | ],
663 | "noOfVideos": 0,
664 | "isPremiumJob": true,
665 | "isHomePageJob": false,
666 | "isDisplayLogoJob": false,
667 | "isTopJob": false,
668 | "categories": {
669 | "function": [
670 | [
671 | "Senior Management & Heads of Department",
672 | [
673 | "Other Senior Management"
674 | ]
675 | ],
676 | [
677 | "Academic Posts",
678 | [
679 | "Postdocs",
680 | "Studentships"
681 | ]
682 | ]
683 | ],
684 | "location": [
685 |
686 | ]
687 | },
688 | "urlId": "1300\/business-travel-reservations",
689 | "usesAltRecruiterName": false
690 | },
691 | {
692 | "id": 1301,
693 | "title": "Business Travel Reservations",
694 | "posted": "16 Apr 2015",
695 | "description": "",
696 | "recruiter": "Transport for London (Madgex Test Data)",
697 | "recruiterID": 21,
698 | "recruiterLogo": "8bc4bed9-0fc3-42b3-a3a0-920e195f8ca4",
699 | "salary": "\u00a3110,001 - \u00a3120,000",
700 | "location": "S65 4DA, Rotherham",
701 | "geo": [
702 |
703 | ],
704 | "jobRole": null,
705 | "applyInfo": {
706 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1301\/business-travel-reservations\/?trackid=22",
707 | "type": "response-management",
708 | "allowMobileExternalRedirects": true
709 | },
710 | "upsells": [
711 |
712 | ],
713 | "noOfVideos": 0,
714 | "isPremiumJob": false,
715 | "isHomePageJob": false,
716 | "isDisplayLogoJob": false,
717 | "isTopJob": false,
718 | "categories": {
719 | "function": [
720 | [
721 | "Senior Management & Heads of Department",
722 | [
723 | "Other Senior Management"
724 | ]
725 | ],
726 | [
727 | "Academic Posts",
728 | [
729 | "Postdocs",
730 | "Studentships"
731 | ]
732 | ]
733 | ],
734 | "location": [
735 |
736 | ]
737 | },
738 | "urlId": "1301\/business-travel-reservations",
739 | "usesAltRecruiterName": false
740 | },
741 | {
742 | "id": 1305,
743 | "title": "Tele-Sales",
744 | "posted": "17 Apr 2015",
745 | "description": "",
746 | "recruiter": "Sky (Madgex Test Data)",
747 | "recruiterID": 19,
748 | "recruiterLogo": "d3031aac-8566-4227-ae58-c72811cc20e6",
749 | "salary": "\u00a370,001 - \u00a380,000",
750 | "location": "LE5 5TH, Leicester",
751 | "geo": [
752 |
753 | ],
754 | "jobRole": null,
755 | "applyInfo": {
756 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1305\/tele-sales\/?trackid=22",
757 | "type": "email-applications",
758 | "allowMobileExternalRedirects": true
759 | },
760 | "upsells": [
761 |
762 | ],
763 | "noOfVideos": 0,
764 | "isPremiumJob": false,
765 | "isHomePageJob": false,
766 | "isDisplayLogoJob": false,
767 | "isTopJob": false,
768 | "categories": {
769 | "function": [
770 | [
771 | "Senior Management & Heads of Department",
772 | [
773 | "Chief Operating Officers",
774 | "Directors",
775 | "Other Senior Management"
776 | ]
777 | ],
778 | [
779 | "Professional Services",
780 | [
781 | "Marketing and Communications"
782 | ]
783 | ]
784 | ],
785 | "location": [
786 |
787 | ]
788 | },
789 | "urlId": "1305\/tele-sales",
790 | "usesAltRecruiterName": false
791 | },
792 | {
793 | "id": 1307,
794 | "title": "Health and Safety Officer",
795 | "posted": "17 Apr 2015",
796 | "description": "",
797 | "recruiter": "Sky (Madgex Test Data)",
798 | "recruiterID": 19,
799 | "recruiterLogo": "d3031aac-8566-4227-ae58-c72811cc20e6",
800 | "salary": "\u00a310,001 - \u00a320,000",
801 | "location": "EH12 7UH, Edinburgh",
802 | "geo": [
803 |
804 | ],
805 | "jobRole": null,
806 | "applyInfo": {
807 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1307\/health-and-safety-officer\/?trackid=22",
808 | "type": "email-applications",
809 | "allowMobileExternalRedirects": true
810 | },
811 | "upsells": [
812 | 5,
813 | 6,
814 | 7
815 | ],
816 | "noOfVideos": 0,
817 | "isPremiumJob": true,
818 | "isHomePageJob": false,
819 | "isDisplayLogoJob": false,
820 | "isTopJob": false,
821 | "categories": {
822 | "function": [
823 | [
824 | "Academic Related",
825 | [
826 | "Other Academic Related"
827 | ]
828 | ],
829 | [
830 | "Professional Services",
831 | [
832 | "Knowledge Transfer \/ Business Links",
833 | "Library \/ Records \/ Information Management"
834 | ]
835 | ]
836 | ],
837 | "location": [
838 |
839 | ]
840 | },
841 | "urlId": "1307\/health-and-safety-officer",
842 | "usesAltRecruiterName": false
843 | },
844 | {
845 | "id": 1306,
846 | "title": "Legal Adviser",
847 | "posted": "17 Apr 2015",
848 | "description": "",
849 | "recruiter": "Sky (Madgex Test Data)",
850 | "recruiterID": 19,
851 | "recruiterLogo": "d3031aac-8566-4227-ae58-c72811cc20e6",
852 | "salary": "Daily Rate",
853 | "location": "TR18 2FE, Penzance",
854 | "geo": [
855 |
856 | ],
857 | "jobRole": null,
858 | "applyInfo": {
859 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1306\/legal-adviser\/?trackid=22",
860 | "type": "email-applications",
861 | "allowMobileExternalRedirects": true
862 | },
863 | "upsells": [
864 | 5,
865 | 6
866 | ],
867 | "noOfVideos": 0,
868 | "isPremiumJob": true,
869 | "isHomePageJob": false,
870 | "isDisplayLogoJob": false,
871 | "isTopJob": false,
872 | "categories": {
873 | "function": [
874 | [
875 | "Senior Management & Heads of Department",
876 | [
877 | "Pro Vice-chancellors"
878 | ]
879 | ],
880 | [
881 | "Academic Posts",
882 | [
883 | "Readers \/ Senior Research Fellows"
884 | ]
885 | ],
886 | "Academic Related",
887 | [
888 | "Professional Services",
889 | [
890 | "Administrative",
891 | "Fundraising, Development and Alumni"
892 | ]
893 | ]
894 | ],
895 | "location": [
896 |
897 | ]
898 | },
899 | "urlId": "1306\/legal-adviser",
900 | "usesAltRecruiterName": false
901 | },
902 | {
903 | "id": 1310,
904 | "title": "Plant Manager",
905 | "posted": "20 Apr 2015",
906 | "description": "",
907 | "recruiter": "Marriott Hotels (Madgex Test Data)",
908 | "recruiterID": 15,
909 | "recruiterLogo": "d26e36da-6874-4f36-a183-5a34b1463424",
910 | "salary": "\u00a3130,001 - \u00a3140,000",
911 | "location": "IP28 8AT, Bury St Edmunds",
912 | "geo": [
913 |
914 | ],
915 | "jobRole": null,
916 | "applyInfo": {
917 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1310\/plant-manager\/?trackid=22",
918 | "type": "email-applications",
919 | "allowMobileExternalRedirects": true
920 | },
921 | "upsells": [
922 |
923 | ],
924 | "noOfVideos": 0,
925 | "isPremiumJob": false,
926 | "isHomePageJob": false,
927 | "isDisplayLogoJob": false,
928 | "isTopJob": false,
929 | "categories": {
930 | "function": [
931 | [
932 | "Senior Management & Heads of Department",
933 | [
934 | "Directors"
935 | ]
936 | ],
937 | [
938 | "Academic Posts",
939 | [
940 | "Studentships"
941 | ]
942 | ],
943 | [
944 | "Professional Services",
945 | [
946 | "Finance and Procurement"
947 | ]
948 | ]
949 | ],
950 | "location": [
951 |
952 | ]
953 | },
954 | "urlId": "1310\/plant-manager",
955 | "usesAltRecruiterName": false
956 | },
957 | {
958 | "id": 1311,
959 | "title": "Legal Adviser",
960 | "posted": "20 Apr 2015",
961 | "description": "",
962 | "recruiter": "Sky (Madgex Test Data)",
963 | "recruiterID": 19,
964 | "recruiterLogo": "d3031aac-8566-4227-ae58-c72811cc20e6",
965 | "salary": "Daily Rate",
966 | "location": "TR18 2FE, Penzance",
967 | "geo": [
968 |
969 | ],
970 | "jobRole": null,
971 | "applyInfo": {
972 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1311\/legal-adviser\/?trackid=22",
973 | "type": "email-applications",
974 | "allowMobileExternalRedirects": true
975 | },
976 | "upsells": [
977 |
978 | ],
979 | "noOfVideos": 0,
980 | "isPremiumJob": false,
981 | "isHomePageJob": false,
982 | "isDisplayLogoJob": false,
983 | "isTopJob": false,
984 | "categories": {
985 | "function": [
986 | [
987 | "Senior Management & Heads of Department",
988 | [
989 | "Pro Vice-chancellors"
990 | ]
991 | ],
992 | [
993 | "Academic Posts",
994 | [
995 | "Readers \/ Senior Research Fellows"
996 | ]
997 | ],
998 | "Academic Related",
999 | [
1000 | "Professional Services",
1001 | [
1002 | "Administrative",
1003 | "Fundraising, Development and Alumni"
1004 | ]
1005 | ]
1006 | ],
1007 | "location": [
1008 |
1009 | ]
1010 | },
1011 | "urlId": "1311\/legal-adviser",
1012 | "usesAltRecruiterName": false
1013 | },
1014 | {
1015 | "id": 1324,
1016 | "title": "Vehicle Technician",
1017 | "posted": "20 Apr 2015",
1018 | "description": "",
1019 | "recruiter": "Sky (Madgex Test Data)",
1020 | "recruiterID": 19,
1021 | "recruiterLogo": "d3031aac-8566-4227-ae58-c72811cc20e6",
1022 | "salary": "Daily Rate",
1023 | "location": "HU3 1XR, Kingston upon Hull",
1024 | "geo": [
1025 |
1026 | ],
1027 | "jobRole": null,
1028 | "applyInfo": {
1029 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1324\/vehicle-technician\/?trackid=22",
1030 | "type": "email-applications",
1031 | "allowMobileExternalRedirects": true
1032 | },
1033 | "upsells": [
1034 |
1035 | ],
1036 | "noOfVideos": 0,
1037 | "isPremiumJob": false,
1038 | "isHomePageJob": false,
1039 | "isDisplayLogoJob": false,
1040 | "isTopJob": false,
1041 | "categories": {
1042 | "function": [
1043 | [
1044 | "Senior Management & Heads of Department",
1045 | [
1046 | "Deans"
1047 | ]
1048 | ],
1049 | [
1050 | "Academic Posts",
1051 | [
1052 | "Lecturers \/ Assistant Professors",
1053 | "Postdocs"
1054 | ]
1055 | ],
1056 | [
1057 | "Professional Services",
1058 | [
1059 | "Registry"
1060 | ]
1061 | ]
1062 | ],
1063 | "location": [
1064 |
1065 | ]
1066 | },
1067 | "urlId": "1324\/vehicle-technician",
1068 | "usesAltRecruiterName": false
1069 | },
1070 | {
1071 | "id": 1325,
1072 | "title": "Project Manager",
1073 | "posted": "20 Apr 2015",
1074 | "description": "",
1075 | "recruiter": "Sky (Madgex Test Data)",
1076 | "recruiterID": 19,
1077 | "recruiterLogo": "d3031aac-8566-4227-ae58-c72811cc20e6",
1078 | "salary": "\u00a330,001 - \u00a340,000",
1079 | "location": "WS3 2QE, Walsall",
1080 | "geo": [
1081 |
1082 | ],
1083 | "jobRole": null,
1084 | "applyInfo": {
1085 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1325\/project-manager\/?trackid=22",
1086 | "type": "offline-applications",
1087 | "allowMobileExternalRedirects": true
1088 | },
1089 | "upsells": [
1090 |
1091 | ],
1092 | "noOfVideos": 0,
1093 | "isPremiumJob": false,
1094 | "isHomePageJob": false,
1095 | "isDisplayLogoJob": false,
1096 | "isTopJob": false,
1097 | "categories": {
1098 | "function": [
1099 | [
1100 | "Professional Services",
1101 | [
1102 | "Student Services"
1103 | ]
1104 | ]
1105 | ],
1106 | "location": [
1107 |
1108 | ]
1109 | },
1110 | "urlId": "1325\/project-manager",
1111 | "usesAltRecruiterName": false
1112 | },
1113 | {
1114 | "id": 1230,
1115 | "title": "Deputy Vice-Chancellor (Research and Enterprise)",
1116 | "posted": "21 Apr 2015",
1117 | "description": "",
1118 | "recruiter": "University of Greenwich",
1119 | "recruiterID": 1229,
1120 | "recruiterLogo": "69beea65-72a0-4dde-b5e2-0122b9e4532c",
1121 | "salary": "Attractive six-figure salary",
1122 | "location": "London (Greater)",
1123 | "geo": [
1124 |
1125 | ],
1126 | "jobRole": null,
1127 | "applyInfo": {
1128 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1230\/deputy-vice-chancellor-research-and-enterprise-\/?trackid=22",
1129 | "type": "external-redirect",
1130 | "allowMobileExternalRedirects": true,
1131 | "externalApplicationUrl": "http:\/\/www.odgers.com\/51759"
1132 | },
1133 | "upsells": [
1134 | 8,
1135 | 5,
1136 | 7,
1137 | 6,
1138 | 7
1139 | ],
1140 | "noOfVideos": 0,
1141 | "isPremiumJob": false,
1142 | "isHomePageJob": true,
1143 | "isDisplayLogoJob": true,
1144 | "isTopJob": false,
1145 | "categories": {
1146 | "function": [
1147 | [
1148 | "Senior Management & Heads of Department",
1149 | [
1150 | "Deputy Vice-chancellors"
1151 | ]
1152 | ]
1153 | ],
1154 | "location": [
1155 |
1156 | ]
1157 | },
1158 | "urlId": "1230\/deputy-vice-chancellor-research-and-enterprise-",
1159 | "usesAltRecruiterName": false
1160 | },
1161 | {
1162 | "id": 1228,
1163 | "title": "Research Lecturer\/Senior Lecturer in Artificial Intelligence\/Interactive Systems",
1164 | "posted": "21 Apr 2015",
1165 | "description": "",
1166 | "recruiter": "Teesside University",
1167 | "recruiterID": 1215,
1168 | "recruiterLogo": "cb7c6707-bb9e-464b-b168-e166d4f1e3d5",
1169 | "salary": "\u00a329,552 - \u00a347,328",
1170 | "location": "Middlesbrough, North Yorkshire",
1171 | "geo": [
1172 |
1173 | ],
1174 | "jobRole": null,
1175 | "applyInfo": {
1176 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1228\/research-lecturer-senior-lecturer-in-artificial-intelligence-interactive-systems\/?trackid=22",
1177 | "type": "external-redirect",
1178 | "allowMobileExternalRedirects": true,
1179 | "externalApplicationUrl": "http:\/\/www.tees.ac.uk\/sections\/jobs"
1180 | },
1181 | "upsells": [
1182 | 6
1183 | ],
1184 | "noOfVideos": 0,
1185 | "isPremiumJob": false,
1186 | "isHomePageJob": false,
1187 | "isDisplayLogoJob": true,
1188 | "isTopJob": false,
1189 | "categories": {
1190 | "function": [
1191 | [
1192 | "Academic Posts",
1193 | [
1194 | "Principal \/ Senior Lecturers \/ Associate Professors",
1195 | "Lecturers \/ Assistant Professors"
1196 | ]
1197 | ]
1198 | ],
1199 | "location": [
1200 |
1201 | ]
1202 | },
1203 | "urlId": "1228\/research-lecturer-senior-lecturer-in-artificial-intelligence-interactive-systems",
1204 | "usesAltRecruiterName": false
1205 | },
1206 | {
1207 | "id": 1227,
1208 | "title": "Research Lecturer\/Senior Lecturer in Criminology",
1209 | "posted": "21 Apr 2015",
1210 | "description": "",
1211 | "recruiter": "Teesside University",
1212 | "recruiterID": 1215,
1213 | "recruiterLogo": "cb7c6707-bb9e-464b-b168-e166d4f1e3d5",
1214 | "salary": "\u00a329,552 - \u00a347,328",
1215 | "location": "Middlesbrough, North Yorkshire",
1216 | "geo": [
1217 |
1218 | ],
1219 | "jobRole": null,
1220 | "applyInfo": {
1221 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1227\/research-lecturer-senior-lecturer-in-criminology\/?trackid=22",
1222 | "type": "external-redirect",
1223 | "allowMobileExternalRedirects": true,
1224 | "externalApplicationUrl": "http:\/\/www.tees.ac.uk\/sections\/jobs"
1225 | },
1226 | "upsells": [
1227 | 6
1228 | ],
1229 | "noOfVideos": 0,
1230 | "isPremiumJob": false,
1231 | "isHomePageJob": false,
1232 | "isDisplayLogoJob": true,
1233 | "isTopJob": false,
1234 | "categories": {
1235 | "function": [
1236 | [
1237 | "Academic Posts",
1238 | [
1239 | "Principal \/ Senior Lecturers \/ Associate Professors",
1240 | "Lecturers \/ Assistant Professors"
1241 | ]
1242 | ]
1243 | ],
1244 | "location": [
1245 |
1246 | ]
1247 | },
1248 | "urlId": "1227\/research-lecturer-senior-lecturer-in-criminology",
1249 | "usesAltRecruiterName": false
1250 | },
1251 | {
1252 | "id": 1226,
1253 | "title": "Research Lecturer\/Senior Lecturer in Business and Management",
1254 | "posted": "21 Apr 2015",
1255 | "description": "",
1256 | "recruiter": "Teesside University",
1257 | "recruiterID": 1215,
1258 | "recruiterLogo": "cb7c6707-bb9e-464b-b168-e166d4f1e3d5",
1259 | "salary": "\u00a329,552 - \u00a347,328",
1260 | "location": "Middlesbrough, North Yorkshire",
1261 | "geo": [
1262 |
1263 | ],
1264 | "jobRole": null,
1265 | "applyInfo": {
1266 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1226\/research-lecturer-senior-lecturer-in-business-and-management\/?trackid=22",
1267 | "type": "external-redirect",
1268 | "allowMobileExternalRedirects": true,
1269 | "externalApplicationUrl": "http:\/\/www.tees.ac.uk\/sections\/jobs"
1270 | },
1271 | "upsells": [
1272 | 6
1273 | ],
1274 | "noOfVideos": 0,
1275 | "isPremiumJob": false,
1276 | "isHomePageJob": false,
1277 | "isDisplayLogoJob": true,
1278 | "isTopJob": false,
1279 | "categories": {
1280 | "function": [
1281 | [
1282 | "Academic Posts",
1283 | [
1284 | "Principal \/ Senior Lecturers \/ Associate Professors",
1285 | "Lecturers \/ Assistant Professors"
1286 | ]
1287 | ]
1288 | ],
1289 | "location": [
1290 |
1291 | ]
1292 | },
1293 | "urlId": "1226\/research-lecturer-senior-lecturer-in-business-and-management",
1294 | "usesAltRecruiterName": false
1295 | },
1296 | {
1297 | "id": 1225,
1298 | "title": "Research Lecturer\/Senior Lecturer in Social Policy\/Sociology",
1299 | "posted": "21 Apr 2015",
1300 | "description": "",
1301 | "recruiter": "Teesside University",
1302 | "recruiterID": 1215,
1303 | "recruiterLogo": "cb7c6707-bb9e-464b-b168-e166d4f1e3d5",
1304 | "salary": "\u00a329,552 - \u00a347,328",
1305 | "location": "Middlesbrough, North Yorkshire",
1306 | "geo": [
1307 |
1308 | ],
1309 | "jobRole": null,
1310 | "applyInfo": {
1311 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1225\/research-lecturer-senior-lecturer-in-social-policy-sociology\/?trackid=22",
1312 | "type": "external-redirect",
1313 | "allowMobileExternalRedirects": true,
1314 | "externalApplicationUrl": "http:\/\/www.tees.ac.uk\/sections\/jobs"
1315 | },
1316 | "upsells": [
1317 | 6
1318 | ],
1319 | "noOfVideos": 0,
1320 | "isPremiumJob": false,
1321 | "isHomePageJob": false,
1322 | "isDisplayLogoJob": true,
1323 | "isTopJob": false,
1324 | "categories": {
1325 | "function": [
1326 | [
1327 | "Academic Posts",
1328 | [
1329 | "Principal \/ Senior Lecturers \/ Associate Professors",
1330 | "Lecturers \/ Assistant Professors"
1331 | ]
1332 | ]
1333 | ],
1334 | "location": [
1335 |
1336 | ]
1337 | },
1338 | "urlId": "1225\/research-lecturer-senior-lecturer-in-social-policy-sociology",
1339 | "usesAltRecruiterName": false
1340 | },
1341 | {
1342 | "id": 1224,
1343 | "title": "Research Lecturer\/Senior Lecturer in Digital Media & Culture",
1344 | "posted": "21 Apr 2015",
1345 | "description": "",
1346 | "recruiter": "Teesside University",
1347 | "recruiterID": 1215,
1348 | "recruiterLogo": "cb7c6707-bb9e-464b-b168-e166d4f1e3d5",
1349 | "salary": "\u00a329,552 - \u00a347,328",
1350 | "location": "Middlesbrough, North Yorkshire",
1351 | "geo": [
1352 |
1353 | ],
1354 | "jobRole": null,
1355 | "applyInfo": {
1356 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1224\/research-lecturer-senior-lecturer-in-digital-media-and-culture\/?trackid=22",
1357 | "type": "external-redirect",
1358 | "allowMobileExternalRedirects": true,
1359 | "externalApplicationUrl": "http:\/\/www.tees.ac.uk\/sections\/jobs"
1360 | },
1361 | "upsells": [
1362 | 6
1363 | ],
1364 | "noOfVideos": 0,
1365 | "isPremiumJob": false,
1366 | "isHomePageJob": false,
1367 | "isDisplayLogoJob": true,
1368 | "isTopJob": false,
1369 | "categories": {
1370 | "function": [
1371 | [
1372 | "Academic Posts",
1373 | [
1374 | "Principal \/ Senior Lecturers \/ Associate Professors",
1375 | "Lecturers \/ Assistant Professors"
1376 | ]
1377 | ]
1378 | ],
1379 | "location": [
1380 |
1381 | ]
1382 | },
1383 | "urlId": "1224\/research-lecturer-senior-lecturer-in-digital-media-and-culture",
1384 | "usesAltRecruiterName": false
1385 | },
1386 | {
1387 | "id": 1223,
1388 | "title": "Research Lecturer\/Senior Lecturer in Health Sciences Research (Systematic Reviewing)",
1389 | "posted": "21 Apr 2015",
1390 | "description": "",
1391 | "recruiter": "Teesside University",
1392 | "recruiterID": 1215,
1393 | "recruiterLogo": "cb7c6707-bb9e-464b-b168-e166d4f1e3d5",
1394 | "salary": "\u00a329,552 - \u00a347,328",
1395 | "location": "Middlesbrough, North Yorkshire",
1396 | "geo": [
1397 |
1398 | ],
1399 | "jobRole": null,
1400 | "applyInfo": {
1401 | "applyUrl": "http:\/\/guidesmiths-web.madgexjbtest.com\/apply\/1223\/research-lecturer-senior-lecturer-in-health-sciences-research-systematic-reviewing-\/?trackid=22",
1402 | "type": "external-redirect",
1403 | "allowMobileExternalRedirects": true,
1404 | "externalApplicationUrl": "http:\/\/www.tees.ac.uk\/sections\/jobs"
1405 | },
1406 | "upsells": [
1407 | 6
1408 | ],
1409 | "noOfVideos": 0,
1410 | "isPremiumJob": false,
1411 | "isHomePageJob": false,
1412 | "isDisplayLogoJob": true,
1413 | "isTopJob": false,
1414 | "categories": {
1415 | "function": [
1416 | [
1417 | "Academic Posts",
1418 | [
1419 | "Principal \/ Senior Lecturers \/ Associate Professors",
1420 | "Lecturers \/ Assistant Professors"
1421 | ]
1422 | ]
1423 | ],
1424 | "location": [
1425 |
1426 | ]
1427 | },
1428 | "urlId": "1223\/research-lecturer-senior-lecturer-in-health-sciences-research-systematic-reviewing-",
1429 | "usesAltRecruiterName": false
1430 | }
1431 | ]
1432 | }
1433 | }
--------------------------------------------------------------------------------