Dayalan Punniyamoorthy Blog

Sunday, February 20, 2022

KSCOPE 21 - Takeaways and programs/code used in my presentation - Part 1

Welcome back to my Blog. Hope you find this one useful & informative.

I did present this topic in KSCOPE 21 -  and as promised during the session will be posting all the programs and the code that is used during the presentation.

In this post, I will post the EPM Automate program and the Groovy Program to automate security management.

Also, you could find my presentation here.




EPM Automate Program

---------------------------------------------------
@echo off

echo Batch command: %0 %*
echo Batch Started at %date% %time%

REM other Parameters
SET Produrl=https://PRODURL.oraclecloud.com
SET TESTurl=https://TESTURL.oraclecloud.com
SET domain=DOMAIN NAME 
SET user=YOUR EMAIL ID
SET password=C:\Users\admin\Documents\Kscope21\password1.epw
setlocal EnableDelayedExpansion

REM EPM Automate Commands
echo CD /D C:\Oracle\EPM Automate\bin\ at  %weekday% %date% %time%
CD /D C:\Oracle\EPM Automate\bin\

REM Login to Pod
call epmautomate login %user% %password% %TESTurl% %domain%
IF %ERRORLEVEL% NEQ 0 goto :ERRORLEVEL%

REM Export App Security  
echo epmautomate exportAppSecurity app_security.csv
call epmautomate exportAppSecurity app_security.csv
IF %ERRORLEVEL% NEQ 0 goto :ERRORLEVEL%

REM Downaload Exported App Security  
echo epmautomate downloadFile app_security.csv
call epmautomate downloadFile app_security.csv
IF %ERRORLEVEL% NEQ 0 goto :ERRORLEVEL%

REM Export Cell Level Security
echo epmautomate exportCellLevelSecurity ExportCLSDRecordsFile.zip
call epmautomate exportCellLevelSecurity ExportCLSDRecordsFile.zip
IF %ERRORLEVEL% NEQ 0 goto :ERRORLEVEL%

REM Download Exported Cell Level Security
echo epmautomate downloadFile ExportCLSDRecordsFile.zip
REM call epmautomate downloadFile ExportCLSDRecordsFile.zip
IF %ERRORLEVEL% NEQ 0 goto :ERRORLEVEL%

REM epmautomate sendMail  "YOUR EMAIL ID" "App Level Security and CLS is exported and downladed"  Body="Success"

echo call epmautomate logout at %date% %time%
call epmautomate logout
IF %ERRORLEVEL% NEQ 0 goto :ERRORLEVEL%

echo Successfully
exit /b %errorlevel%
:ERROR

echo Failed with error #%errorlevel%.
exit /b %errorlevel%

ENDLOCAL
------------------------------------------------


Groovy Program

------------------------------------------------
import java.nio.charset.StandardCharsets

import groovy.json.JsonSlurper

//Reading the Values from the Properties File
Properties props = new Properties()
File propsFile = new File('C:/Users/admin/Documents/Kscope21/PBCSDetails.properties')
props.load(propsFile.newDataInputStream())

serverUrl=props.getProperty('serverUrl')
domain=props.getProperty('domain')
username=props.getProperty('username')
password=props.getProperty('password')
interopUrl=props.getProperty('interopUrl') 

serverUrl=props.getProperty('serverUrl')
username=props.getProperty('username')
password=props.getProperty('password')

InteropApiVersion="11.1.2.3.600";
//serverFileName="RoleAssignmentReport6.csv";
localFolderForStorage="C:/"
//filename="RoleAssignmentReport2.csv";
//fileName="RoleAssignmentReport22Jun21.csv";
//path="C:\\Usersdmin\\Documents\\Kscope21\\"
path="C:\\Users\\admin\\Documents\\Kscope21\\"
//path="C:\\Users\\DPunniamoorthy.VECG041\\OneDrive - Vertical Edge Consulting Group\\Documents\\RESTAPI-Webinar\\Groovy\\"
//filename="UserGroupReport.csv"
filename="UserGroupReport0.csv"
apiVersion = "v1";

userCredentials = username + ":" + password;
basicAuth = "Basic " + javax.xml.bind.DatatypeConverter.printBase64Binary(userCredentials.getBytes())

def getResponse(is) {
BufferedReader br = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line+"\n");
}
br.close();
return sb.toString();
}

def getUrlFromResponse(scenario, response, relValue) {
def object = new JsonSlurper().parseText(response)
def pingUrlStr
if (object.status == -1) {
println "Started - " + scenario
def links = object.links
links.each{
if (it.rel.equals(relValue)) {
pingUrlStr=it.href
}
}
} else {
println "Error details: " + object.details
System.exit(0);
}
return pingUrlStr
}

def getJobStatus(pingUrlString, methodType) {

def pingUrl = new URL(pingUrlString);
def completed = false;
while (!completed) {
pingResponse = executeRequest(pingUrl, methodType, null, "application/x-www-form-urlencoded");
status = getJobStatusFromResponse(pingResponse);
if (status == "Processing") {
try {
println "Processing. Please wait..."
Thread.sleep(5000);
} catch (InterruptedException e) {
completed = true
}
} else {
println status
completed = true
}
}
}

def getJobStatusFromResponse(response) {
def object = new JsonSlurper().parseText(response)
def status = object.status
if (status == -1)
return "Processing"
else if (status == 0)
return "Completed"
else
return object.details
}

def getJobDetailsFromResponse(response) {
def object = new JsonSlurper().parseText(response)
def details = object.details
if (details != null)
return object.details
else
return null
}

def executeRequest(url, requestType, payload, contentType) {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setInstanceFollowRedirects(false);
connection.setRequestMethod(requestType);
connection.setRequestProperty("Content-Type", contentType);
//           connection.setRequestProperty("charset", StandardCharsets.UTF_8);
connection.setRequestProperty("Authorization", basicAuth);
connection.setUseCaches(false);

if (payload != null) {
OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
writer.write(payload);
writer.flush();
}

int statusCode
try {
statusCode = connection.responseCode;
} catch (all) {
println "Error connecting to the URL"
System.exit(0);
}

def response
if (statusCode == 200 || statusCode == 201) {
if (connection.getContentType() != null && !connection.getContentType().startsWith("application/json")) {
println "Error occurred in server"
System.exit(0)
}
InputStream is = connection.getInputStream();
if (is != null)
response = getResponse(is)
} else {
println "Error occurred while executing request"
println "Response error code : " + statusCode
InputStream is = connection.getErrorStream();
if (is != null && connection.getContentType() != null && connection.getContentType().startsWith("application/json"))
println getJobStatusFromResponse(getResponse(is))
System.exit(0);
}
connection.disconnect();
return response;
}

def addUsersToGroup(fileName, groupName) {

String scenario = "Adding users in " + fileName + " to group " + groupName;
String params = "jobtype=ADD_USERS_TO_GROUP&filename="+ fileName +"&groupname="+ groupName;
def url = null;
def response = null;
try {
url = new URL(serverUrl + "/interop/rest/security/" + apiVersion + "/groups");
} catch (MalformedURLException e) {
println "Please enter a valid URL"
System.exit(0);
}
response = executeRequest(url, "PUT", params, "application/x-www-form-urlencoded");
if (response != null) {
getJobStatus(getUrlFromResponse(scenario, response, "Job Status"), "GET");
}
}

def removeUsersFromGroup(fileName, groupName) {

String scenario = "Removing users in " + fileName + " from group " + groupName;
String params = "jobtype=REMOVE_USERS_FROM_GROUP&filename="+ fileName +"&groupname="+ groupName;
def url = null;
def response = null;
try {
url = new URL(serverUrl + "/interop/rest/security/" + apiVersion + "/groups");
} catch (MalformedURLException e) {
println "Please enter a valid URL"
System.exit(0);
}
response = executeRequest(url, "PUT", params, "application/x-www-form-urlencoded");
if (response != null) {
getJobStatus(getUrlFromResponse(scenario, response, "Job Status"), "GET");
}
}

def addUserToGroups(fileName, userName) {

String scenario = "Adding users in " + fileName + " to group " + userName;
String params = "jobtype=ADD_USER_TO_GROUPS&filename="+ fileName +"&username="+ userName;
def url = null;
def response = null;
try {
url = new URL(serverUrl + "/interop/rest/security/" + apiVersion + "/groups");
} catch (MalformedURLException e) {
println "Please enter a valid URL"
System.exit(0);
}
response = executeRequest(url, "PUT", params, "application/x-www-form-urlencoded");
if (response != null) {
getJobStatus(getUrlFromResponse(scenario, response, "Job Status"), "GET");
}
}

def removeUserFromGroups(fileName, userName) {

String scenario = "Removing users in " + fileName + " from group " + userName;
String params = "jobtype=REMOVE_USER_FROM_GROUPS&filename="+ fileName +"&username="+ userName;
def url = null;
def response = null;
try {
url = new URL(serverUrl + "/interop/rest/security/" + apiVersion + "/groups");
} catch (MalformedURLException e) {
println "Please enter a valid URL"
System.exit(0);
}
response = executeRequest(url, "PUT", params, "application/x-www-form-urlencoded");
if (response != null) {
getJobStatus(getUrlFromResponse(scenario, response, "Job Status"), "GET");
}
}

def addUsers(fileName, resetPassword, userPassword) {

String scenario = "Creating users in " + fileName;
String params = "jobtype=ADD_USERS&filename="+ fileName +"&resetpassword="+ resetPassword +"&userpassword="+ userPassword;
def url = null;
def response = null;
try {
url = new URL(serverUrl + "/interop/rest/security/" + apiVersion + "/users");
} catch (MalformedURLException e) {
println "Please enter a valid URL"
System.exit(0);
}
response = executeRequest(url, "POST", params, "application/x-www-form-urlencoded");
if (response != null) {
getJobStatus(getUrlFromResponse(scenario, response, "Job Status"), "GET");
}
}

def addUsers(fileName) {
addUsers(fileName, null, null);
}

def deleteUsers(fileName) {

String scenario = "Deleting users in " + fileName;
String params = null;
def url = null;
def response = null;
try {
url = new URL(serverUrl + "/interop/rest/security/" + apiVersion + "/users?filename=" + fileName);
} catch (MalformedURLException e) {
println "Please enter a valid URL"
System.exit(0);
}
response = executeRequest(url, "DELETE", null, "application/x-www-form-urlencoded");
if (response != null) {
getJobStatus(getUrlFromResponse(scenario, response, "Job Status"), "GET");
}
}

def assignUsersRoles(fileName, roleName) {

String scenario = "Assigning users in " + fileName + " with role " + roleName;
String params = "jobtype=ASSIGN_ROLE&filename="+ fileName +"&rolename="+ roleName;
def url = null;
def response = null;
try {
url = new URL(serverUrl + "/interop/rest/security/" + apiVersion + "/users");
} catch (MalformedURLException e) {
println "Please enter a valid URL"
System.exit(0);
}
response = executeRequest(url, "PUT", params, "application/x-www-form-urlencoded");
if (response != null) {
getJobStatus(getUrlFromResponse(scenario, response, "Job Status"), "GET");
}
}

def unassignUsersRoles(fileName, roleName) {

String scenario = "Un-assigning users in " + fileName + " with role " + roleName;
String params = "jobtype=UNASSIGN_ROLE&filename="+ fileName +"&rolename="+ roleName;
def url = null;
def response = null;
try {
url = new URL(serverUrl + "/interop/rest/security/" + apiVersion + "/users");
} catch (MalformedURLException e) {
println "Please enter a valid URL"
System.exit(0);
}
response = executeRequest(url, "PUT", params, "application/x-www-form-urlencoded");
if (response != null) {
getJobStatus(getUrlFromResponse(scenario, response, "Job Status"), "GET");
}
}

def generateRoleAssignmentReport(fileName) {

String scenario = "Generating Role assignment report in " + fileName;
String params = "jobtype=GENERATE_ROLE_ASSIGNMENT_REPORT&filename="+ fileName;
def url = null;
def response = null;
try {
url = new URL(serverUrl + "/interop/rest/security/" + apiVersion + "/roleassignmentreport");
} catch (MalformedURLException e) {
println "Please enter a valid URL"
System.exit(0);
}
response = executeRequest(url, "POST", params, "application/x-www-form-urlencoded");
if (response != null) {
getJobStatus(getUrlFromResponse(scenario, response, "Job Status"), "GET");
}
}

def generateUserGroupReport(fileName) {

String scenario = "Generating User Group Report in " + fileName;
String params = "jobtype=GENERATE_USER_GROUP_REPORT&filename="+ fileName;
def url = null;
def response = null;
try {
url = new URL(serverUrl + "/interop/rest/security/" + apiVersion + "/usergroupreport");
} catch (MalformedURLException e) {
println "Please enter a valid URL"
System.exit(0);
}
response = executeRequest(url, "POST", params, "application/x-www-form-urlencoded");
if (response != null) {
getJobStatus(getUrlFromResponse(scenario, response, "Job Status"), "GET");
}
}

def addGroups(fileName) {
println "addgroups"
String scenario = "Creating Groups in " + fileName;
String params = "filename="+ fileName;
def url = null;
def response = null;
try {
url = new URL(serverUrl + "/interop/rest/security/" + apiVersion + "/groups");
} catch (MalformedURLException e) {
println "Please enter a valid URL"
System.exit(0);
}
response = executeRequest(url, "POST", params, "application/x-www-form-urlencoded");
if (response != null) {
getJobStatus(getUrlFromResponse(scenario, response, "Job Status"), "GET");
}
}

def removeGroups(fileName) {

String scenario = "Deleting Groups in " + fileName;
String params = null;
def url = null;
def response = null;
try {
url = new URL(serverUrl + "/interop/rest/security/" + apiVersion + "/groups?filename=" + fileName);
} catch (MalformedURLException e) {
println "Please enter a valid URL"
System.exit(0);
}
response = executeRequest(url, "DELETE", null, "application/x-www-form-urlencoded");
if (response != null) {
getJobStatus(getUrlFromResponse(scenario, response, "Job Status"), "GET");
}
}

def generateRoleAssignmentAuditReport(from_date,to_date,fileName) {

String scenario = "Generating Role assignment audit report in " + fileName;
String params = "jobtype=GENERATE_ROLE_ASSIGNMENT_AUDIT_REPORT&from_date="+from_date+"&to_date="+to_date+"&filename="+ fileName;
def url = null;
def response = null;
try {
url = new URL(serverUrl + "/interop/rest/security/" + apiVersion + "/roleassignmentauditreport");
} catch (MalformedURLException e) {
println "Please enter a valid URL"
System.exit(0);
}
response = executeRequest(url, "POST", params, "application/x-www-form-urlencoded");
if (response != null) {
getJobStatus(getUrlFromResponse(scenario, response, "Job Status"), "GET");
}
}

def generateInvalidLoginReport(from_date,to_date,fileName) {

String scenario = "Generating Invalid Login report in " + fileName;
String params = "jobtype=GENERATE_INVALID_LOGIN_REPORT&from_date="+from_date+"&to_date="+to_date+"&filename="+ fileName;
def url = null;
def response = null;
try {
url = new URL(serverUrl + "/interop/rest/security/" + apiVersion + "/invalidloginreport");
} catch (MalformedURLException e) {
println "Please enter a valid URL"
System.exit(0);
}
response = executeRequest(url, "POST", params, "application/x-www-form-urlencoded");
if (response != null) {
getJobStatus(getUrlFromResponse(scenario, response, "Job Status"), "GET");
}
}

/***************************************************************/
def listFiles() {
 def url;
 try {
 url = new URL(serverUrl + "/interop/rest/" + InteropApiVersion + "/applicationsnapshots")
//println url
 } catch (MalformedURLException e) {
 println "Malformed URL. Please pass valid URL"
 System.exit(0);
 }
 response = executeRequest(url, "GET", null, "application/x-www-form-urlencoded")
// println  response
 def object = new JsonSlurper().parseText(response)
 def status = object.status
 if (status == 0 ) {
 def items = object.items
// println items
 def ItName = items.name
 def LastModififedTime = items.lastModififedTime

 def Date = LastModififedTime
 
/* For Converting Time 
var d = new LastModififedTime;
var formattedDate = d.getDate() + "-" + (d.getMonth() + 1) + "-" + d.getFullYear();
var hours = (d.getHours() < 10) ? "0" + d.getHours() : d.getHours();
var minutes = (d.getMinutes() < 10) ? "0" + d.getMinutes() : d.getMinutes();
var formattedTime = hours + ":" + minutes;

formattedDate = formattedDate + " " + formattedTime;
 
*/ 
 
// def Size = items.size
 if (ItName == null) {
 //if (items == null) {
 println "No files found"
}
 else {

 String[] test = items.name
 def arrayLength = test.length
 def count = arrayLength
 println "No of files Present in the Pod is : " + arrayLength + "\n"
 println "List of files :"
 for (int i=0; i<count; i++) {
// println "Name : " + ItName[i] +   " LastModififedTime: " +  LastModififedTime[i] 
  println "Name : " + ItName[i] 
// items.each{
//println ItName
// println it.name
 }
 }
 } else {
 println "Error occurred while listing files"
 if (object.details != null)
println "Error details: " + object.details
 // println arrayLength
 }
}


def deleteFile(filename) {
 def url;
 try {
 String encodedFileName = URLEncoder.encode(filename, "UTF-8");
 url = new URL(serverUrl + "/interop/rest/" + InteropApiVersion + "/applicationsnapshots/" + encodedFileName)
 } catch (MalformedURLException e) {
 println "Malformed URL. Please pass valid URL"
 System.exit(0);
 }
 response = executeRequest(url, "DELETE", null,"application/x-www-form-urlencoded");
 def object = new JsonSlurper().parseText(response)
 def status = object.status
 if (status == 0 )
 println "File deleted successfully :" +filename
 else {
 println "Error occurred while deleting file"
 if (object.details != null)
 println "Error details: " + object.details
 }
}
/***************************************************************/

def uploadFile(localPath,fileName) {
println "**Uploading file**"
//def restUrl=interopUrl+"/applicationsnapshots/"+fileName
                    def restUrl=serverUrl + "/interop/rest/" + InteropApiVersion +"/applicationsnapshots/"+fileName
final int DEFAULT_CHUNK_SIZE = 50 * 1024 * 1024;
int packetNo = 1;
boolean status = true;
byte[] lastChunk = null;
File f = new File(localPath+"/"+fileName);
InputStream fis = null;
long totalFileSize = f.length();
boolean isLast = false;
Boolean isFirst = true;
boolean firstRetry = true;
int lastPacketNo = (int) (Math.ceil(totalFileSize/ (double) DEFAULT_CHUNK_SIZE));
long totalbytesRead = 0;
try {
fis = new BufferedInputStream(new FileInputStream(localPath+"/"+fileName));
while (totalbytesRead < totalFileSize && status) {
int nextChunkSize = (int) Math.min(DEFAULT_CHUNK_SIZE, totalFileSize - totalbytesRead);
if (lastChunk == null) {
lastChunk = new byte[nextChunkSize];
int bytesRead = fis.read(lastChunk);
totalbytesRead += bytesRead;
if (packetNo == lastPacketNo) {
isLast = true;
}
status = sendRequestToRestForUpload(restUrl,isFirst, isLast,lastChunk);
isFirst=false;
if (status) {
println "\r" + ((100 * totalbytesRead)/ totalFileSize) + "% completed";
} else {
break;
}
packetNo = packetNo + 1;
lastChunk = null;
}
}
} catch (Exception e) {
println "Exception occurred while uploading file";
println e.getMessage()
} finally {
if (null != fis) {
}
}
println "****"
}
def sendRequestToRestForUpload(restUrl,isFirst, isLast,lastChunk) {
def url=restUrl+"/contents?q={isLast:$isLast,chunkSize:"+lastChunk.length+",isFirst:$isLast}"
println "Opening connection for upload to $url"
int statusCode
// setProxyParams()
// setSSLParams()
URL newUrl
newUrl=new URL(url)
connection = (HttpURLConnection) newUrl.openConnection()
connection.setDoOutput(true)
connection.setDoInput(true)
connection.setUseCaches(false)
connection.setRequestMethod("POST")
connection.setRequestProperty("Content-Type","application/octet-stream")
String userCredentials = domain + "." + username + ":" + password;
String basicAuth = "Basic " + javax.xml.bind.DatatypeConverter.printBase64Binary(userCredentials.getBytes())
connection.setRequestProperty("Authorization", basicAuth)
DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
wr.write(lastChunk);
wr.flush();
boolean status = false
int execStatus
try {
execStatus = connection.getResponseCode();
InputStream is = connection.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line+"\n");
}
br.close();
String stat = sb.toString();
if (null == stat || stat.isEmpty()) {
return status;
} else {
if (200 == execStatus) {
println stat
}
}
} catch (Exception e) {
println "Exception occurred while uploading file";
println e.getMessage()
} finally {
if (connection != null) {
connection.disconnect();
}
}
}

/***************************************************************/
// Download File Function
def downloadFile(filename) {
def url;
try {
String encodedFileName = URLEncoder.encode(filename, "UTF-8");
url = new URL(serverUrl + "/interop/rest/" +  InteropApiVersion + "/applicationsnapshots/" + encodedFileName + "/contents");
//println url
} catch (MalformedURLException e) {
println "Malformed URL. Please pass valid URL"
System.exit(0);
}
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setUseCaches(false);
connection.setRequestMethod("GET");
connection.setRequestProperty("Authorization", basicAuth);
int statusCode
try {
statusCode = connection.responseCode;
} catch (all) {
println "Error connecting to the URL"
System.exit(0);
}
if (statusCode == 200) {
InputStream is;
if (connection.getContentType() != null &&connection.getContentType().contains("application/json")) {
is = connection.getInputStream();
if (is != null) {
response = getResponse(is)
println response
def object = new JsonSlurper().parseText(response)
//println "Error occurred while downloading file"
if (object.details != null)
println "Error details: " + object.details
}
} else {
final int BUFFER_SIZE = 5 * 1024 * 1024;
saveFilePath = path + filename
File f = new File(saveFilePath);
is = connection.getInputStream();
FileOutputStream outputStream = new FileOutputStream(f);
int bytesRead = -1;
byte[] buffer = new byte[BUFFER_SIZE];
while ((bytesRead = is.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
println "Downloaded "  + filename + " successfully";
}
} else {
println "Error occurred while executing request"
println "Response error code : " + statusCode
InputStream is = connection.getErrorStream();
if (is != null && connection.getContentType() != null &&connection.getContentType().startsWith("application/json"))
println fetchJobStatusFromResponse(fetchResponse(is))
System.exit(0);
}
connection.disconnect();
}




def Test() {
println "Test"
}

//Execute commands here
//addUsersToGroup("RegionEntities.csv", "KOL Asia locations");                                                                                           //PUT
//removeUsersFromGroup("Users.csv", "G1");                                                                            //PUT
//addUsers("AddUsers123.csv", "false", "newPassword");                                        //POST
//addUsers("AddUsers456.csv");                                                                                                                                 //POST
//deleteUsers("RemoveUsers.csv");                                                                                                             //DELETE
//assignUsersRoles("Users.csv", "Power User");                           //PUT
//assignUsersRoles("users.csv", "viewer");                                                          //PUT
//unassignUsersRoles("Users.csv", "Drill Through");                                                   //PUT
// generateRoleAssignmentReport("RoleAssignmentReport.csv"); // POST
//generateUserGroupReport("UserGroupReport.csv"); // POST
//addUserToGroups("APGroups.csv", "days.jan@email.com");                                               //PUT
//removeUserFromGroups("groups.csv", "joe");                                          //PUT
//addGroups("CreateGroup1.csv"); // POST
//removeGroups("DeleteGroup1.csv"); // DELETE
//generateInvalidLoginReport("2021-10-01", "2021-11-10", "InvalidLoginReport.csv"); //POST
listFiles()
// deleteFile("UserGroupReport0.csv");
//downloadFile("ZUserGroupReport0.csv");
//*****************************************************
//generateRoleAssignmentReport("ZRoleAssignmentReport0.csv"); // POST
//generateUserGroupReport("ZUserGroupReport0.csv"); // POST
//generateInvalidLoginReport("2021-10-01", "2021-11-10", "ZInvalidLoginReport0.csv"); //POST
//Test();


------------------------------------------------

2 comments:

  1. you nee open for you self GitHub

    ReplyDelete
  2. TDL Grab Hire Ltd are one of the most reliable and trusted aggregates suppliers in Doncaster, with over 50 years in the industry you can rely on the fact that we deliver a prompt service, and you aggregate delivery in Doncaster will arrive on time at a location that is convenient to you. We will deliver both large and small loads and have a wide range of stock at our dedicated yard and we ensure the most competitive prices.
    Aggregate Delivery Doncaster

    ReplyDelete