The diagnosis.php page below, is requested via an AJAX HTTPRequest(), and it is displayed on a div in the same calling page. The ICD's javascript resource located at https://icdcdn.who.int/embeddedct/icd11ect-1.6.1.js is defined in the same calling page.
Console information below shows that, there is a successful connection to the ICD API script.
However, the API call returns error 'Invalid Token Specified';
How do I correctly integrate this API, with proper calls to the API server in php?
ECT v1.6.1 - React v17.0.2 icd11ect-1.6.1.js:1 waiting for setting the last minor version icd11ect-1.6.1.js:1 autoBind: true 2icd11ect-1.6.1.js:1 waiting for setting the last minor version icd11ect-1.6.1.js:1 loading... 2icd11ect-1.6.1.js:1 waiting for setting the last minor version icd11ect-1.6.1.js:1 searchMethod set to: POST icd11ect-1.6.1.js:1 setting the last minor version: 2023-01 icd11ect-1.6.1.js:1 loading... icd11ect-1.6.1.js:1 ICDAPI Initialization Completed: true
diagnosis.php `
<?php session_start(); require('../../../conn/connection.php'); $pid = $_REQUEST['pid']; $cfid = $_REQUEST['cfid'];?>
<script>
var mySettings = myCallbacks = null;
</script>
<link rel="stylesheet" href="https://icdcdn.who.int/embeddedct/icd11ect-1.6.1.css">
<div class="container-fluid tab-pane">
<h3 class="mt-3 mb-3">General Diagnosis</h3>
<hr />
<a href="#addNewDiagnosisDiv" onclick="showDiagnosisDiv()" class="link mt-2 mb-2">Add New Diagnosis </a>
<div class="container-fluid mt-3 mb-3" id="newDiagnosisDiv" style="display:none; border-style:solid; border-color:#ddd; border-radius:5px; padding: 10px 10px;">
<div class="row">
<div class="col-3">
<div class="form-group">
<select id="ICDSearchCategory" onchange="showOrHideType(this.value)">
<option value='Confirmed (ICD10)'> Confirmed (ICD10) </option>
<option value='Confirmed (Unclassified)'> Confirmed (Unclassified) </option>
<option value='Query (Unconfirmed)'> Query (Unconfirmed) </option>
</select>
</div>
</div>
<div class="col" id="showSearchType">
<div class="form-check-inline">
<label class="form-check-label"> <span class="text-danger">*</span> Thesaurus:
<!--input type="radio" class="form-check-input" id="optradio" name="optradio" value="local" disabled >Local
</label-->
</div>
<div class="form-check-inline">
<label class="form-check-label">
<input type="radio" class="form-check-input" id="optradio" name="optradio" value="global" checked>Global
</label>
</div>
<div class="form-check-inline">
<input type="text" class="ctw-input" autocomplete="off" data-ctw-ino="1" placeholder="Enter a keyword to search the global ICD10 database">
</div>
<div class="ctw-window" data-ctw-ino="1"></div>
<div class="form-check-inline">
<a class="btn text-primary" id="btnAdd" onclick="saveDiagnosis(<?php echo $_REQUEST['cfid']; ?>,<?php echo $_REQUEST['pid']; ?>)">+ Add </a>
</div>
</div>
</div>
<div class="row">
<div class="col-2">
<h5 class="text-primary"><span class="text-danger">*</span> ICD10 Code: <span class="small text-muted" id="showICD10Code"></span></h5>
</div>
<div class="col">
<h5 class="text-primary"><span class="text-danger">*</span> ICD10 Description: <span class="small text-muted" id="showICD10Desc"></span></h5>
</div>
</div>
</div>
<div class="container-fluid mt-2 mb-2" id="showDianosisTable">
<table class="table-responsive table-striped" style="width:100%;">
<thead class="text-center bg-secondary text-white">
<th style="width:5%;">Status</th> <th style="width:19%;">Description</th> <th style="width:19%;">Category</th>
<th style="width:19%;">Thesaurus Description</th> <th style="width:19%;">Updated By</th> <th style="width:19%;">Diagnosis Date</th>
</thead>
<tbody class="text-center">
<?php $cat = mysqli_query($con, "SELECT * FROM diagnosis WHERE pid='".$_REQUEST['pid']."' ") or die(mysqli_error($con)); $count = 0;
if(mysqli_num_rows($cat) > 0){
while($r = mysqli_fetch_array($cat)){ $count += 1; ?>
<tr>
<td class="text-center" style=""><button class="btn btn-danger bg-white text-danger" id="did" onclick="updateDiagnosisStatus(<?php echo $r['diagid'];?>, '<?php echo $r['status']; ?>')"> <?php echo $r['status']; ?> </button></td>
<td class="text-left" style=""> <?php echo $r['description']; ?></td>
<td class="text-left" style=""> <?php echo $r['category']; ?></td>
<td class="text-left" style=""> <?php echo $r['thesaurus']; ?></td>
<td class="text-left" style=""> <?php echo $r['diagnosedby']; ?></td>
<td class="text-left" style=""> <?php echo $r['diagnosisdate']; ?></td>
</tr>
<?php }
} ?>
</tbody>
</table>
</div>
<input type="hidden" id="mycfid" value="<?php echo $_REQUEST['cfid'];?>" /> <input type="hidden" id="mypid" value="<?php echo $_REQUEST['pid'];?>" />
<div class="alert text-left" id="diasalert"> </div>
</div>
<script>
mySettings = {
// The API located at the URL below should be used only for software
// development and testing. ICD content at this location might not
// be up to date or complete. For production, use the API located at
// id.who.int with proper OAUTH authentication
// apiServerUrl: "https://icd11restapi-developer-test.azurewebsites.net", apiSecured: false,
apiServerUrl: "https://id.who.int", apiSecured: true,
enableKeyboard: true,
popupMode: true,
sourceApp: "NMSL Mosimi FPP Portal",
language: "en" // set the language to English
};
// example of an Embedded Coding Tool using the callback selectedEntityFunction
// for copying the code selected in an <input> element and clear the search results
myCallbacks = {
selectedEntityFunction: (selectedEntity) => {
// {"iNo":"1",
// "uri":"http://id.who.int/icd/release/11/2023-01/mms/1439886552/unspecified",
// "linearizationUri":"http://id.who.int/icd/release/11/2023-01/mms/1439886552/unspecified",
// "foundationUri":"http://id.who.int/icd/entity/1439886552",
// "code":"1F4Z",
// "title":"Malaria, unspecified",
// "bestMatchText":"Malaria, unspecified",
// "selectedText":"Malaria, unspecified",
// "searchQuery":"Malaria"}
// paste the code into the <input>
// document.getElementById('paste-selectedEntity').value = selectedEntity.code+'; '+selectedEntity.title;
document.getElementById('showICD10Code').innerHTML = selectedEntity.code;
document.getElementById('showICD10Desc').innerHTML = selectedEntity.title;
// clear the searchbox and delete the search results
ECT.Handler.clear("1");
},
getNewTokenFunction: async () => {
// if the embedded coding tool is working with the cloud hosted ICD-API, you need to set apiSecured=true
// In this case the Embedded Coding Tool calls this function when it needs a new token.
// So, your backend web application should provide updated tokens
const url = 'icdbackend/index.php' // we assume this backend script returns a JSON {'token': '...'}
const x = new XMLHttpRequest();
try {
x.onreadystatechange = function(){
// console.log('State: ', this.readyState);
// console.log('Status: ', this.status);
console.log('Response: ', this.responseText);
if(this.readyState === 4 && this.status === 200){
const result = this.responseText;
const token = result.token;
}
}; x.open("GET", url, true); x.send();
// const response = await fetch(url);
// const result = await response.json();
// const token = result.token;
// return token; // the function return is required
} catch (e) {
console.log("Error during the request");
}
},
searchStartedFunction: () => {
//this callback is called when searching is started.
console.log("Search started!");
},
searchEndedFunction: () => {
//this callback is called when search ends.
console.log("Search ended!");
}
};
// configure the ECT Handler with mySettings and myCallbacks
ECT.Handler.configure(mySettings, myCallbacks);
</script>
<script>
var currentStatus = "none";
function showDiagnosisDiv(){ //alert();
if (currentStatus === 'none'){
$("#newDiagnosisDiv").show(); currentStatus = 'block';
}else if(currentStatus === 'block'){
$("#newDiagnosisDiv").hide(); currentStatus = 'none';
}
}
function showOrHideType(value){
var x = new XMLHttpRequest();
x.onreadystatechange = function(){
if(this.readyState === 4 && this.status === 200){
// $("#showSearchType").html(this.responseText);
}
}; x.open("GET", "processalldata.php?d=show ICD search type&v="+value, true); x.send();
}
function saveDiagnosis(c,p){ var cfid = $("#mycfid").val(); var pid = $("#mypid").val(); //alert(pid);
var searchCat = $("#ICDSearchCategory").val();
if(searchCat === 'Confirmed (ICD10)'){
var icd10code = $("#showICD10Code").html(); var icd10desc = $("#showICD10Desc").html();
var description = $("#showICD10Code").html() + " - " + $("#showICD10Desc").html(); var category = searchCat; var thesaurusDesc = $("#thesaurus").val();
}else if(searchCat === 'Confirmed (Unclassified)' || searchCat === 'Query (Unconfirmed)'){
var description = $("#diagnosisKeyword").val(); var category = searchCat; var thesaurusDesc = '';
var icd10code = $("#showICD10Code").html(); var icd10desc = $("#showICD10Desc").html();
}
// $("#diasalert").html(cfid + " - " + pid);
var x = new XMLHttpRequest(); var f = new FormData();
f.append("icd10code", icd10code); f.append("icd10desc", icd10desc); f.append("desc", description); f.append("cat", category); f.append("thes", thesaurusDesc);
f.append("conName", "Consultant Name"); f.append("date", "Date of Diagnosis"); f.append("cfid", cfid); f.append("pid", pid); f.append("st", 'Diagnosis');
x.onreadystatechange = function(){
if(this.readyState === 4 && this.status === 200){
if(this.responseText.includes("This diagnosis already exists as Active.") === true){
var resLeft = this.responseText.split("|")[0];
$("#diasalert").html(resLeft); return false;
}else{
$("#showDianosisTable").html(this.responseText);
}
}
}; x.open("POST", "processalldata.php?d=save and load diagnosis", true); x.send(f);
}
function fetchICDAPIValues(value){
}
function updateDiagnosisStatus(id, cStatus){ var pid = $("#mypid").val();
if(cStatus === 'Active'){ cStatus = 'Inactive'; }else { cStatus = 'Active'; }
var x = new XMLHttpRequest(); x.onreadystatechange = function(){
if(this.readyState === 4 && this.status === 200){ //alert(this.responseText);
if(this.responseText.includes("This diagnosis already exists as Active.") === true){
var resLeft = this.responseText.split("|")[0];
$("#diasalert").html(resLeft); return false;
}else{
$("#showDianosisTable").html(this.responseText);
}
}
}; x.open("GET", "processalldata.php?d=update diagnosis&cs="+cStatus+"&id="+id+"&pid="+pid, true); x.send();
}
</script>
icdbackend/index.php
<?php
$cid = '80d4f4af-4fe7-4986-bafb-28d968659195_ed6d67ab-09b9-47aa-86a7-32d2471acd26';
$csec = "SPLScANIWqiz/gZD0xvREB74ABS0s7yjoKAu5c3u2XA=";
$tokenEndpoint = "https://icdaccessmanagement.who.int/connect/token";
$clientId = "$cid";
$clientSecret = "$csec";
$grant_type = "client_credentials";
$scope = "icdapi_access";
$data = array(
'client_id' => $clientId,
'client_secret' => $clientSecret,
'scope' => $scope,
'grant_type' => $grant_type
);
$options = array(
'https' => array(
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
'method' => 'POST',
'content' => http_build_query($data)
)
);
$context = stream_context_create($options);
$result = file_get_contents($tokenEndpoint, false, $context);
if ($result === FALSE) {
$error = error_get_last();
echo "HTTP request failed. Error was: " . $error['message'];
} else {
$json_array = (json_decode($result, true));
$tken = $json_array['access_token'];
// create HTTP context to access ICD API
$options = array(
'https' => array(
'header' => "Authorization: Bearer ".$tken."\r\n".
"Accept: application/json\r\n".
"Accept-Language: en\r\n".
"API-Version: v2"
)
);
$context = stream_context_create($options);
$result = file_get_contents('https://id.who.int/icd/entity', false, $context);
$token = array();
if ($result === FALSE) {
$error = error_get_last();
echo "HTTP request failed. Error was: " . $error['message'];
} else {
// $token[] = $result; echo json_encode($token);
$token = array('token' => $tken); echo json_encode($token);
}
}
?>
Actually, the 'invalid token specified' error emanates from this line...
apiServerUrl: "https://id.who.int", apiSecured: true,
However, if I change the apiServerUrl to 'https://icd11restapi-developer-test.azurewebsites.net' and set
apeSecured to true, athus:
// apiServerUrl: "https://icd11restapi-developer-test.azurewebsites.net", apiSecured: false,
the API returns another type of error,
'Uncaught (in promise) Error: A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received'