Skip to content

Commit

Permalink
big changes
Browse files Browse the repository at this point in the history
  • Loading branch information
jakub-bao committed Oct 18, 2023
2 parents e2c7435 + 7cf9cda commit d39796a
Show file tree
Hide file tree
Showing 126 changed files with 52,628 additions and 15,697 deletions.
6 changes: 4 additions & 2 deletions datim-api/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {getJson,getText, getBlob} from "./services/methods/get.service";
import {getBaseUrl, isTestEnv, register, setTestUsername} from "./services/config.service";
import {getAuthorization, getBaseUrl, getFullUrl, isTestEnv, register, setTestUsername} from "./services/config.service";
import {registerGetMock} from "./services/mock/getMock.serivce";
import {initTestCache} from "./services/cache/getCache.service";
import {postJson, postText, putJson, postEmpty, patchJson} from "./services/methods/sendData.service";
Expand All @@ -23,6 +23,8 @@ export {
setTestUsername,
register,
getBaseUrl,
getFullUrl,
deleteData,
isTestEnv
isTestEnv,
getAuthorization
}
4 changes: 2 additions & 2 deletions datim-api/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion datim-api/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@pepfar-react-lib/datim-api",
"version": "0.0.18",
"version": "0.0.25",
"description": "JS wrapper around DATIM API",
"main": "index.ts",
"scripts": {
Expand Down
7 changes: 4 additions & 3 deletions datim-api/services/methods/delete.service.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import {getFullUrl, isTestEnv} from "../config.service";
import {mockSendingData} from "../mock/sendMock.service";
import {inspectResponse} from "./inspectResponse.service";
import {ApiResponse} from "../../types/http.types";


export function deleteData(endpointUrl:string,options?:any){
export function deleteData(endpointUrl:string,options?:any):Promise<ApiResponse>{
if (isTestEnv()) return mockSendingData(endpointUrl,{});
return fetch(getFullUrl(endpointUrl),{
method: 'DELETE',
credentials: 'include',
...options
})
}).then(inspectResponse)
}
64 changes: 18 additions & 46 deletions datim-api/services/methods/inspectResponse.service.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,32 @@
import {ApiResponse, ErrorType} from "../../types/http.types";
import {ApiResponse} from "../../index";
import {ErrorType} from "../../types/http.types";

function getErrorMessage(errorType:ErrorType, apiResponse:ApiResponse):string{
export function getErrorMessage(errorType:ErrorType):string{
switch (errorType){
case ErrorType.cannotParse:
return `Unsupported server response. Cannot retrieve response body.`
case ErrorType.ignored:
return 'Ignored'
case ErrorType.silentRedirect:
return `Wrong request. Response status ${apiResponse.rawResponse.status}, Redirected to ${apiResponse.rawResponse.url}\n\tThis likely means either wrong url or invalid authentication`
return `Redirected to login`
case ErrorType.httpError:
return `Response status ${apiResponse.rawResponse.status} ${apiResponse.rawResponse.statusText}\n\tUrl: ${apiResponse.rawResponse.url}`
return `HTTP error`
case ErrorType.alreadyExists:
return `Object already exists. ${apiResponse.responseBody.typeReports[0].objectReports[0].errorReports[0].message}`
case ErrorType.dhis2ErrorSpecified:
return `Server response: ${apiResponse.responseBody.typeReports[0].objectReports[0].errorReports[0].message}`
case ErrorType.dhis2ErrorUnspecified:
return `Server responded with status 200 but error in response body. Cannot retrieve error message.`
return `Object already exists`
}
}

class FailService {
apiResponse:ApiResponse;
responseBody:any;
constructor(apiResponse:ApiResponse) {
this.apiResponse = apiResponse;
}
fail(errorType:ErrorType):ApiResponse{
this.apiResponse.success = false;
this.apiResponse.errorType = errorType;
this.apiResponse.errorMessage = getErrorMessage(errorType, this.apiResponse)
return this.apiResponse;
}
success():ApiResponse{
this.apiResponse.success = true;
return this.apiResponse;
}
function fail(errorType:ErrorType, serverResponse:any):Error{
return Error(getErrorMessage(errorType),{cause:serverResponse});
}

export async function inspectResponse(rawResponse:Response|any):Promise<ApiResponse>{
let apiResponse:ApiResponse = {
success: false,
rawResponse
}
let failService = new FailService(apiResponse);
if (!rawResponse.ok) return failService.fail(ErrorType.httpError);
if (rawResponse.redirected&&rawResponse.url.includes('login')) return failService.fail(ErrorType.silentRedirect)
if (rawResponse.status===204&&!rawResponse.redirected) return failService.success();
export async function inspectResponse(rawResponse:any):Promise<ApiResponse>{
let responseBody = rawResponse;
try {
let responseBody:any = JSON.parse(await rawResponse.text() as any);
failService.apiResponse.responseBody = responseBody;
if (responseBody.status==='ERROR') try {
if (responseBody.typeReports[0].objectReports[0].errorReports[0].message.includes('matching')) return failService.fail(ErrorType.alreadyExists)
else return failService.fail(ErrorType.dhis2ErrorSpecified)
} catch(e){
return failService.fail(ErrorType.dhis2ErrorUnspecified)
}
responseBody = JSON.parse(await rawResponse.text() as any);
} catch (e){
return failService.fail(ErrorType.cannotParse);
}
return failService.success();
if (!rawResponse.ok) throw fail(ErrorType.httpError,responseBody);
if (rawResponse.redirected&&rawResponse.url.includes('login')) throw fail(ErrorType.silentRedirect,responseBody)
if (responseBody.status==='ERROR') throw fail(ErrorType.alreadyExists,responseBody)
if (responseBody.status==='WARNING') throw fail(ErrorType.ignored,responseBody)
return {success:true, responseBody, rawResponse};
}
7 changes: 2 additions & 5 deletions datim-api/services/methods/sendData.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,5 @@ function sendData(method:HttpMethod,endpoint:string,contentType:ContentType,payl
'Content-Type':contentType
},
body: contentType===ContentType.json?JSON.stringify(payload):payload
}).then(inspectResponse).then((apiResponse:ApiResponse)=>{
if (!apiResponse.success) throw new Error(apiResponse.errorMessage);
else return apiResponse;
})
}
}).then(inspectResponse)
}
4 changes: 3 additions & 1 deletion datim-api/services/mock/sendMock.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ let sendMockService = new MockService();
type SendMockRecord = {
response:any,
resolve:(payload:any)=>void,
reject: (error:any)=>void,
success
}

Expand All @@ -18,7 +19,8 @@ export function mockSendingData(url:string,payload:any):Promise<ApiResponse>{
if (!isSendMocked(url)) throw new Error(`POST/PUT mock not in place`);
let {resolve, response,success}:SendMockRecord = sendMockService.getMockedResponse(url);
resolve(payload);
return Promise.resolve({responseBody:response,success,rawResponse:null});
if (success) return Promise.resolve({responseBody:response,success,rawResponse:null});
else return Promise.reject();
}

export function isSendMocked(url:string):boolean{
Expand Down
8 changes: 4 additions & 4 deletions datim-api/test/1.getJson.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import datimApi from "../index";
import {testCredentials} from "./testCredentials.const";
import fetch from "node-fetch"
import {Environment} from "../types/config.type";
import {register, setTestUsername, getJson} from "../index";
// @ts-ignore
global.fetch = fetch

test(`1 > getJson`,async ()=>{
datimApi.register(Environment.test, testCredentials.url);
datimApi.setTestUsername(testCredentials.username,testCredentials.auth);
let response:{id:string} = await datimApi.getJson(`/me`);
register(Environment.test, testCredentials.url);
setTestUsername(testCredentials.username,testCredentials.auth);
let response:{id:string} = await getJson(`/me`);
expect(response.id.length).toBe(11);
})
10 changes: 5 additions & 5 deletions datim-api/test/2.getMock.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import datimApi from "../index";
import {register, setTestUsername, getJson, registerGetMock} from "../index";
import {Environment} from "../types/config.type";
import {testCredentials} from "./testCredentials.const";

test(`2 > Get Mock`, async ()=>{
datimApi.register(Environment.test, '');
datimApi.setTestUsername(testCredentials.username,testCredentials.auth);
datimApi.registerGetMock(`/users/test`,{name: 'test'});
let response = await datimApi.getJson(`/users/test`);
register(Environment.test, '');
setTestUsername(testCredentials.username,testCredentials.auth);
registerGetMock(`/users/test`,{name: 'test'});
let response = await getJson(`/users/test`);
expect(response).toStrictEqual({name: 'test'});
})
16 changes: 8 additions & 8 deletions datim-api/test/3.sendMock.test.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import datimApi from "../index";
import {register, setTestUsername, postJson,registerSendMock} from "../index";
import {Environment} from "../types/config.type";
import {testCredentials} from "./testCredentials.const";

test(`3 > Send mock > mock invite`, async ()=>{
datimApi.register(Environment.test, '');
datimApi.setTestUsername(testCredentials.username,testCredentials.auth);
let payloadPromise = datimApi.registerSendMock(`/users/invite`,{response: 'success'});
let response = await datimApi.postJson(`/users/invite`,{request: 'user'});
register(Environment.test, '');
setTestUsername(testCredentials.username,testCredentials.auth);
let payloadPromise = registerSendMock(`/users/invite`,{response: 'success'});
let response = await postJson(`/users/invite`,{request: 'user'});
expect(response.responseBody).toStrictEqual({response: 'success'});
expect(await payloadPromise).toStrictEqual({request: 'user'});
});

test(`3 > Send mock > error: not mocked in test`, async ()=>{
datimApi.register(Environment.test, '');
datimApi.setTestUsername(testCredentials.username,testCredentials.auth);
await expect(async ()=>datimApi.postJson(`/users/123`,{request: 'user'})).rejects.toThrow();
register(Environment.test, '');
setTestUsername(testCredentials.username,testCredentials.auth);
await expect(async ()=>postJson(`/users/123`,{request: 'user'})).rejects.toThrow();
});
94 changes: 94 additions & 0 deletions datim-api/test/4.inspectResponse.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import {ErrorType, getErrorMessage, inspectResponse} from "../index";

type TestCase = {
input: any,
success: boolean,
name: string,
errorType?: ErrorType,
responseBody?:any
}

let alreadyExistsError = {
status: 'ERROR',
typeReports:[{
objectReports: [{
errorReports: [{
message: 'found matching object with id xyz'
}]
}]
}]
}

let ignoredWarning = {
status: 'WARNING',
importCount:{ignored:1}
}

let successResponse = {
inserted: 1
}

const testCases:TestCase[] = [{
name: 'fetch returns {ok:false}',
input: {
ok: false
},
success: false,
errorType: ErrorType.httpError,
},{
name: 'status 301, redirected to login',
input: {
ok: true,
redirected: true,
url: 'https://jakub.datim.org/dhis-web-commons/security/login.action'
},
success: false,
errorType: ErrorType.silentRedirect,
},{
name: 'status 204',
input: {
ok:true,
status: 204
},
success: true,
responseBody: undefined
},{
name: 'DHIS2 error > object already exists',
input: {
ok:true,
text: ()=>Promise.resolve(JSON.stringify(alreadyExistsError))
},
success: false,
errorType: ErrorType.alreadyExists
},{
name: 'DHIS2 warning > object ignored',
input: {
ok:true,
text: ()=>Promise.resolve(JSON.stringify(ignoredWarning))
},
success: false,
errorType: ErrorType.ignored
},{
name: 'success with empty response body',
input: {
ok:true,
text: ()=>Promise.resolve(undefined)
},
success: true,
responseBody: undefined,
},{
name: 'success with response body',
input: {
ok:true,
text: ()=>Promise.resolve(JSON.stringify(successResponse))
},
success: true,
responseBody: successResponse,
}];

testCases.forEach(({name, input, success, responseBody, errorType})=>{
test(`4 > Inspect Response > ${name}`, async ()=>{
if (success) expect((await inspectResponse(input)).responseBody).toStrictEqual(responseBody)
else await expect(inspectResponse(input)).rejects.toThrow(Error(getErrorMessage(errorType)));
})
})
4 changes: 1 addition & 3 deletions datim-api/types/http.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@ export enum ContentType {
}

export enum ErrorType {
cannotParse,
silentRedirect,
httpError,
alreadyExists,
dhis2ErrorSpecified,
dhis2ErrorUnspecified
ignored
}

export type ApiResponse = {
Expand Down
10 changes: 10 additions & 0 deletions datimuser/build/const/regExpMatchers.const.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export declare const userTypeReRules: {
Partner: string;
"Global Partner": string;
Agency: string;
"Global Agency": string;
Global: string;
"Inter-Agency": string;
MOH: string;
System: string;
};
14 changes: 14 additions & 0 deletions datimuser/build/const/regExpMatchers.const.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.userTypeReRules = void 0;
const userType_type_1 = require("../types/userType.type");
exports.userTypeReRules = {
[userType_type_1.UserType.Partner]: '^OU .+ Partner .+ users',
[userType_type_1.UserType.GlobalPartner]: '^Global Partner .+ users',
[userType_type_1.UserType.Agency]: '^OU .+ Agency .+ users',
[userType_type_1.UserType.GlobalAgency]: '^Global Agency .+ users',
[userType_type_1.UserType.Global]: '^Global users',
[userType_type_1.UserType.InterAgency]: '^OU .+ Interagency users',
[userType_type_1.UserType.MOH]: '^OU .+ MOH users$',
[userType_type_1.UserType.System]: '^System users$'
};
7 changes: 7 additions & 0 deletions datimuser/build/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export * from "./types/userType.type";
export * from "./types/dhisUser.type";
export * from "./services/determineUserType.service";
export * from "./services/getTypes.service";
export * from "./services/determineOrganization.service";
export * from "./services/determineUserAdministrator.service";
export * from "./services/isSuperUser.service";
Loading

0 comments on commit d39796a

Please sign in to comment.