Skip to content
GitLab
Explore
Projects
Groups
Topics
Snippets
Projects
Groups
Topics
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Register
Sign in
Toggle navigation
Menu
UPSMF
program-service
Commits
b5d99a0c
Commit
b5d99a0c
authored
4 years ago
by
venkanagouda
Browse files
Options
Download
Patches
Plain Diff
Issue #DP-509 feat: generate tableData of program
parent
2600911d
SB-23374
SB-25924
copy_issue
copy_issue1
dependabot/npm_and_yarn/src/ajv-6.12.6
dependabot/npm_and_yarn/src/async-2.6.4
dependabot/npm_and_yarn/src/axios-0.21.2
dependabot/npm_and_yarn/src/css-what-5.0.1
dependabot/npm_and_yarn/src/decode-uri-component-0.2.2
dependabot/npm_and_yarn/src/elliptic-6.5.4
dependabot/npm_and_yarn/src/jsonwebtoken-9.0.0
dependabot/npm_and_yarn/src/lodash-4.17.21
dependabot/npm_and_yarn/src/moment-2.29.4
dependabot/npm_and_yarn/src/moment-timezone-0.5.37
dependabot/npm_and_yarn/src/node-fetch-2.6.7
dependabot/npm_and_yarn/src/nth-check-2.0.1
dependabot/npm_and_yarn/src/path-parse-1.0.7
dependabot/npm_and_yarn/src/pathval-1.1.1
dependabot/npm_and_yarn/src/qs-and-body-parser-and-express-6.5.3
dependabot/npm_and_yarn/src/redis-3.1.1
dependabot/npm_and_yarn/src/simple-get-2.8.2
dependabot/npm_and_yarn/src/underscore-1.12.1
eslint_fixes
log_issue
master
program-service
publish_changes
questionset-integration
release-3.8.0
release-3.9.0
release-4.0.0
release-4.1.0
release-4.1.1
release-4.2.0
release-4.3.0
release-4.4.0
release-4.5.0
release-4.6.0
release-4.7.0
release-4.8.0
release-4.9.0
release-5.1.0
revert-147-release-3.9.0
sb-22518
sb-25146
sprint-10
sprint-14
sprint-15
sprint-16
sprint-17
sprint-18
sprint-19
sprint-20
sprint-9
test-issue
vk-local-ps-4.4.0
sprint20_RC2
sprint20_RC1
sprint19_RC3
sprint19_RC2
sprint19_RC1
sprint18_RC1
sprint17_RC2
sprint17_RC1
sprint16_RC3
sprint16_RC2
sprint16_RC1
sprint15.1_RC2
sprint15.1_RC1
sprint15_RC4
sprint15_RC3
sprint15_RC2
sprint15_RC1
sprint14_RC1
sprint13_RC4
sprint13_RC3
sprint13_RC2
release-5.1.0_RC1
release-4.9.0_RC1
release-4.8.0_RC3
release-4.8.0_RC2
release-4.8.0_RC1
release-4.7.0_RC6
release-4.7.0_RC5
release-4.7.0_RC4
release-4.7.0_RC3
release-4.7.0_RC2
release-4.7.0_RC1
release-4.6.0_RC6
release-4.6.0_RC5
release-4.6.0_RC4
release-4.6.0_RC3
release-4.6.0_RC2
release-4.6.0_RC1
release-4.5.0_RC1
release-4.4.0_RC5
release-4.4.0_RC4
release-4.4.0_RC3
release-4.4.0_RC2
release-4.4.0_RC1
release-4.3.0_RC5
release-4.3.0_RC4
release-4.3.0_RC3
release-4.3.0_RC2
release-4.3.0_RC1
release-4.2.0_RC9
release-4.2.0_RC8
release-4.2.0_RC7
release-4.2.0_RC6
release-4.2.0_RC5
release-4.2.0_RC4
release-4.2.0_RC3
release-4.2.0_RC2
release-4.2.0_RC1
release-4.1.1_RC1
release-4.1.0_RC2
release-4.1.0_RC1
release-4.0.0_RC3
release-4.0.0_RC2
release-4.0.0_RC1
release-3.9.0_RC4
release-3.9.0_RC3
release-3.9.0_RC2
release-3.9.0_RC1
release-3.8.0_RC3
release-3.8.0_RC2
release-3.8.0_RC1
program-service_RC22
program-service_RC21
program-service_RC20
program-service_RC19
program-service_RC18
program-service_RC17
program-service_RC16
program-service_RC15
No related merge requests found
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
src/helpers/programHelper.js
+168
-0
src/helpers/programHelper.js
src/routes/programRoutes.js
+4
-0
src/routes/programRoutes.js
src/service/messageUtil.js
+12
-0
src/service/messageUtil.js
src/service/programService.js
+95
-35
src/service/programService.js
with
279 additions
and
35 deletions
+279
-35
src/helpers/programHelper.js
0 → 100644
+
168
−
0
View file @
b5d99a0c
const
_
=
require
(
"
lodash
"
);
const
envVariables
=
require
(
"
../envVariables
"
);
const
axios
=
require
(
"
axios
"
);
const
model
=
require
(
'
../models
'
);
const
Sequelize
=
require
(
'
sequelize
'
);
class
ProgramServiceHelper
{
searchWithProgramId
(
queryFilter
,
req
)
{
const
headers
=
{
'
content-type
'
:
'
application/json
'
,
};
const
option
=
{
url
:
`
${
envVariables
.
baseURL
}
/api/composite/v1/search`
,
method
:
'
post
'
,
headers
:
{...
req
.
headers
,
...
headers
},
data
:
{
request
:
queryFilter
}
};
return
axios
(
option
);
}
getCollectionWithProgramId
(
program_id
,
req
)
{
const
queryFilter
=
{
filters
:
{
programId
:
program_id
,
objectType
:
'
content
'
,
status
:
[
'
Review
'
,
'
Draft
'
],
contentType
:
'
Textbook
'
},
fields
:
[
'
name
'
,
'
medium
'
,
'
gradeLevel
'
,
'
subject
'
,
'
chapterCount
'
,
'
acceptedContents
'
,
'
rejectedContents
'
],
limit
:
1000
};
return
this
.
searchWithProgramId
(
queryFilter
,
req
);
}
getSampleContentWithProgramId
(
program_id
,
req
)
{
const
queryFilter
=
{
filters
:
{
programId
:
program_id
,
objectType
:
'
content
'
,
status
:
[
'
Review
'
,
'
Draft
'
],
sampleContent
:
true
},
facets
:
[
'
sampleContent
'
,
'
collectionId
'
,
'
status
'
],
limit
:
0
};
return
this
.
searchWithProgramId
(
queryFilter
,
req
);
}
getContributionWithProgramId
(
program_id
,
req
)
{
const
queryFilter
=
{
filters
:
{
programId
:
program_id
,
objectType
:
'
content
'
,
status
:
[
'
Review
'
,
'
Draft
'
,
'
Live
'
],
contentType
:
{
'
!=
'
:
'
Asset
'
},
mimeType
:
{
'
!=
'
:
'
application/vnd.ekstep.content-collection
'
}
},
not_exists
:
[
'
sampleContent
'
],
aggregations
:
[
{
"
l1
"
:
"
collectionId
"
,
"
l2
"
:
"
status
"
}
],
limit
:
0
};
return
this
.
searchWithProgramId
(
queryFilter
,
req
);
}
getNominationWithProgramId
(
programId
)
{
const
facets
=
[
'
collection_ids
'
,
'
status
'
];
const
promise
=
model
.
nomination
.
findAll
({
where
:
{
program_id
:
programId
},
attributes
:
[...
facets
,
[
Sequelize
.
fn
(
'
count
'
,
Sequelize
.
col
(
facets
[
0
])),
'
count
'
]],
group
:
[...
facets
]
})
return
promise
;
}
handleMultiProgramDetails
(
resGroup
)
{
const
multiProgramDetails
=
_
.
map
(
resGroup
,
(
resData
)
=>
{
try
{
return
this
.
prepareTableData
(
resData
);
}
catch
(
err
)
{
throw
err
}
});
return
multiProgramDetails
;
}
prepareTableData
(
resData
)
{
try
{
const
collectionList
=
resData
[
0
].
data
.
result
&&
resData
[
0
].
data
.
result
.
content
||
[],
sampleContentResponse
=
resData
[
1
].
data
.
result
&&
resData
[
1
].
data
.
result
.
facets
||
[],
contributionResponse
=
resData
[
2
].
data
.
result
&&
resData
[
2
].
data
.
result
.
aggregations
||
[],
nominationResponse
=
_
.
isArray
(
resData
[
3
])
&&
resData
[
3
].
length
?
_
.
map
(
resData
[
3
],
obj
=>
obj
.
dataValues
)
:
[];
let
tableData
=
[];
if
(
collectionList
.
length
)
{
tableData
=
_
.
map
(
collectionList
,
(
collection
)
=>
{
const
result
=
{};
// sequence of columns in tableData
result
[
'
Textbook Name
'
]
=
collection
.
name
;
result
[
'
Medium
'
]
=
collection
.
medium
||
'
--
'
;
result
[
'
Class
'
]
=
collection
.
gradeLevel
&&
collection
.
gradeLevel
.
length
?
collection
.
gradeLevel
.
join
(
'
,
'
)
:
''
;
result
[
'
Subject
'
]
=
collection
.
subject
||
'
--
'
;
result
[
'
Number of Chapters
'
]
=
collection
.
chapterCount
||
'
--
'
;
result
[
'
Nominations Received
'
]
=
0
;
result
[
'
Samples Received
'
]
=
0
;
result
[
'
Nominations Accepted
'
]
=
0
;
result
[
'
Contributions Received
'
]
=
0
;
result
[
'
Contributions Accepted
'
]
=
collection
.
acceptedContents
?
collection
.
acceptedContents
.
length
:
0
;
result
[
'
Contributions Rejected
'
]
=
collection
.
rejectedContents
?
collection
.
rejectedContents
.
length
:
0
;
result
[
'
Contributions Pending
'
]
=
0
;
// count of sample contents
if
(
sampleContentResponse
.
length
)
{
const
facetObj
=
_
.
find
(
sampleContentResponse
,
{
name
:
'
collectionId
'
});
if
(
facetObj
&&
facetObj
.
values
.
length
&&
_
.
find
(
facetObj
.
values
,
{
name
:
collection
.
identifier
}))
{
result
[
'
Samples Received
'
]
=
_
.
find
(
facetObj
.
values
,
{
name
:
collection
.
identifier
}).
count
;
}
}
// count of contribution
if
(
contributionResponse
.
length
&&
contributionResponse
[
0
].
name
===
'
collectionId
'
&&
contributionResponse
[
0
].
values
.
length
)
{
const
statusCount
=
_
.
find
(
contributionResponse
[
0
].
values
,
{
name
:
collection
.
identifier
});
if
(
statusCount
&&
statusCount
.
aggregations
&&
statusCount
.
aggregations
.
length
)
{
_
.
forEach
(
statusCount
.
aggregations
[
0
].
values
,
(
obj
)
=>
{
if
(
obj
.
name
===
'
live
'
)
{
result
[
'
Contributions Received
'
]
=
result
[
'
Contributions Received
'
]
+
obj
.
count
;
// tslint:disable-next-line:max-line-length
result
[
'
Contributions Pending
'
]
=
result
[
'
Contributions Received
'
]
-
(
result
[
'
Contributions Rejected
'
]
+
result
[
'
Contributions Accepted
'
]);
}
});
}
}
// count of nomination
if
(
nominationResponse
.
length
)
{
_
.
forEach
(
nominationResponse
,
(
obj
)
=>
{
if
(
obj
.
collection_ids
&&
_
.
includes
(
obj
.
collection_ids
,
collection
.
identifier
)
)
{
if
(
obj
.
status
===
'
Approved
'
)
{
result
[
'
Nominations Accepted
'
]
=
result
[
'
Nominations Accepted
'
]
+
Number
(
obj
.
count
);
}
else
if
(
obj
.
status
!==
'
Initiated
'
)
{
result
[
'
Nominations Received
'
]
=
result
[
'
Nominations Received
'
]
+
Number
(
obj
.
count
);
}
}
});
result
[
'
Nominations Received
'
]
=
result
[
'
Nominations Accepted
'
]
+
result
[
'
Nominations Received
'
];
}
return
result
;
});
}
return
tableData
;
}
catch
(
err
)
{
throw
'
error in preparing CSV data
'
}
}
}
module
.
exports
=
ProgramServiceHelper
;
This diff is collapsed.
Click to expand it.
src/routes/programRoutes.js
+
4
−
0
View file @
b5d99a0c
...
...
@@ -23,6 +23,10 @@ module.exports = function (app) {
app
.
route
(
BASE_URL
+
'
/list
'
)
.
post
(
requestMiddleware
.
gzipCompression
(),
requestMiddleware
.
createAndValidateRequestBody
,
programService
.
programListAPI
)
app
.
route
(
BASE_URL
+
'
/list/download
'
)
.
post
(
requestMiddleware
.
gzipCompression
(),
requestMiddleware
.
createAndValidateRequestBody
,
programService
.
downloadProgramDetailsAPI
)
app
.
route
(
BASE_URL
+
'
/search
'
)
.
post
(
requestMiddleware
.
gzipCompression
(),
requestMiddleware
.
createAndValidateRequestBody
,
...
...
This diff is collapsed.
Click to expand it.
src/service/messageUtil.js
+
12
−
0
View file @
b5d99a0c
...
...
@@ -672,6 +672,12 @@ exports.PROGRAM = {
FAILED_CODE
:
'
ERR_LINKING_TEXBOOK_FAILED
'
,
FAILED_MESSAGE
:
'
Unable to link textbook to program
'
},
GENERATE_DETAILS
:
{
MISSING_CODE
:
'
ERR_GENERATING_PROGRAM_DATA
'
,
MISSING_MESSAGE
:
'
Required fields like program_id<Array>, is missing
'
,
FAILED_CODE
:
'
ERR_GENERATING_PROGRAM_DATA_FAILED
'
,
FAILED_MESSAGE
:
'
Unable to generate the program data
'
},
NOMINATION
:
{
READ
:
{
MISSING_CODE
:
'
ERR_GET_NOMINATION_DATA
'
,
...
...
@@ -690,6 +696,12 @@ exports.PROGRAM = {
MISSING_MESSAGE
:
'
Required fields like program_id, user_id while updating nomination data are missing
'
,
FAILED_CODE
:
'
ERR_UPDATE_NOMINATION_DATA_FAILED
'
,
FAILED_MESSAGE
:
'
Unable to update the nomination data
'
},
LIST
:
{
MISSING_CODE
:
'
ERR_GET_NOMINATION_LIST
'
,
MISSING_MESSAGE
:
'
Required fields like program_id/user_id to get nomination are missing
'
,
FAILED_CODE
:
'
ERR_GET_NOMINATION_LIST_FAILED
'
,
FAILED_MESSAGE
:
'
Unable to get the nomination list
'
}
},
COPY_COLLECTION
:
{
...
...
This diff is collapsed.
Click to expand it.
src/service/programService.js
+
95
−
35
View file @
b5d99a0c
...
...
@@ -15,6 +15,7 @@ const {
const
axios
=
require
(
'
axios
'
);
const
envVariables
=
require
(
'
../envVariables
'
);
const
RegistryService
=
require
(
'
./registryService
'
)
const
ProgramServiceHelper
=
require
(
'
../helpers/programHelper
'
);
var
async
=
require
(
'
async
'
)
...
...
@@ -22,6 +23,7 @@ const queryRes_Max = 1000;
const
queryRes_Min
=
300
;
const
HierarchyService
=
require
(
'
../helpers/updateHierarchy.helper
'
);
const
programServiceHelper
=
new
ProgramServiceHelper
();
const
registryService
=
new
RegistryService
()
const
hierarchyService
=
new
HierarchyService
()
...
...
@@ -485,6 +487,9 @@ function getNominationsList(req, response) {
var
rspObj
=
req
.
rspObj
;
var
res_limit
=
queryRes_Min
;
var
res_offset
=
data
.
request
.
offset
||
0
;
rspObj
.
errCode
=
programMessages
.
NOMINATION
.
LIST
.
FAILED_CODE
rspObj
.
errMsg
=
programMessages
.
NOMINATION
.
LIST
.
FAILED_MESSAGE
rspObj
.
responseCode
=
responseCode
.
SERVER_ERROR
if
(
data
.
request
.
limit
)
{
res_limit
=
(
data
.
request
.
limit
<
queryRes_Max
)
?
data
.
request
.
limit
:
(
queryRes_Max
);
}
...
...
@@ -506,13 +511,9 @@ function getNominationsList(req, response) {
result
:
result
}))
}).
catch
((
err
)
=>
{
return
response
.
status
(
400
).
send
(
errorResponse
({
apiId
:
'
api.nomination.list
'
,
ver
:
'
1.0
'
,
msgid
:
uuid
(),
responseCode
:
'
ERR_NOMINATION_LIST
'
,
result
:
err
}));
loggerError
(
'
Error fetching nomination count group by facets
'
,
rspObj
.
errCode
,
rspObj
.
errMsg
,
rspObj
.
responseCode
,
err
,
req
);
return
response
.
status
(
400
).
send
(
errorResponse
(
rspObj
));
})
}
else
if
(
data
.
request
.
limit
===
0
)
{
model
.
nomination
.
findAll
({
...
...
@@ -531,13 +532,9 @@ function getNominationsList(req, response) {
result
:
aggregatedRes
}))
}).
catch
((
err
)
=>
{
return
response
.
status
(
400
).
send
(
errorResponse
({
apiId
:
'
api.nomination.list
'
,
ver
:
'
1.0
'
,
msgid
:
uuid
(),
responseCode
:
'
ERR_NOMINATION_LIST
'
,
result
:
err
}));
loggerError
(
'
Error fetching nomination count when limit = 0
'
,
rspObj
.
errCode
,
rspObj
.
errMsg
,
rspObj
.
responseCode
,
err
,
req
);
return
response
.
status
(
400
).
send
(
errorResponse
(
rspObj
));
})
}
else
{
model
.
nomination
.
findAll
({
...
...
@@ -599,35 +596,97 @@ function getNominationsList(req, response) {
result
:
result
}))
},
(
error
)
=>
{
return
response
.
status
(
400
).
send
(
errorResponse
({
apiId
:
'
api.nomination.list
'
,
ver
:
'
1.0
'
,
msgid
:
uuid
(),
responseCode
:
'
ERR_NOMINATION_LIST
'
,
result
:
error
}));
loggerError
(
'
Error in fetching user/org details
'
,
rspObj
.
errCode
,
rspObj
.
errMsg
,
rspObj
.
responseCode
,
error
,
req
);
return
response
.
status
(
400
).
send
(
errorResponse
(
rspObj
));
});
}
catch
(
err
)
{
return
response
.
status
(
400
).
send
(
errorResponse
({
apiId
:
'
api.nomination.list
'
,
ver
:
'
1.0
'
,
msgid
:
uuid
(),
responseCode
:
'
ERR_NOMINATION_LIST
'
,
result
:
err
.
message
||
err
}));
loggerError
(
'
Error fetching nomination with limit and offset
'
,
rspObj
.
errCode
,
rspObj
.
errMsg
,
rspObj
.
responseCode
,
err
,
req
);
return
response
.
status
(
400
).
send
(
errorResponse
(
rspObj
));
}
}).
catch
(
function
(
err
)
{
return
response
.
status
(
400
).
send
(
errorResponse
({
apiId
:
'
api.nomination.list
'
,
ver
:
'
1.0
'
,
msgid
:
uuid
(),
responseCode
:
'
ERR_NOMINATION_LIST
'
,
result
:
err
.
message
||
err
}));
loggerError
(
'
Error fetching nomination with limit and offset
'
,
rspObj
.
errCode
,
rspObj
.
errMsg
,
rspObj
.
responseCode
,
err
,
req
);
return
response
.
status
(
400
).
send
(
errorResponse
(
rspObj
));
});
}
}
async
function
downloadProgramDetails
(
req
,
res
)
{
const
data
=
req
.
body
const
rspObj
=
req
.
rspObj
let
programArr
=
[],
promiseRequests
=
[],
cacheData
=
[],
filteredPrograms
=
[];
rspObj
.
errCode
=
programMessages
.
GENERATE_DETAILS
.
FAILED_CODE
rspObj
.
errMsg
=
programMessages
.
GENERATE_DETAILS
.
FAILED_MESSAGE
rspObj
.
responseCode
=
responseCode
.
SERVER_ERROR
if
(
!
data
.
request
||
!
data
.
request
.
filters
||
!
data
.
request
.
filters
.
program_id
)
{
rspObj
.
errCode
=
programMessages
.
GENERATE_DETAILS
.
MISSING_CODE
rspObj
.
errMsg
=
programMessages
.
GENERATE_DETAILS
.
MISSING_MESSAGE
rspObj
.
responseCode
=
responseCode
.
CLIENT_ERROR
loggerError
(
'
Error due to missing request or request.filters or request.filters.program_id
'
,
rspObj
.
errCode
,
rspObj
.
errMsg
,
rspObj
.
responseCode
,
null
,
req
)
return
res
.
status
(
400
).
send
(
errorResponse
(
rspObj
));
}
programArr
=
_
.
isArray
(
data
.
request
.
filters
.
program_id
)
?
data
.
request
.
filters
.
program_id
:
[];
await
_
.
forEach
(
programArr
,
(
program
)
=>
{
cacheManager
.
get
(
`program_details_
${
program
}
`
,
(
err
,
cache
)
=>
{
if
(
err
||
!
cache
)
{
filteredPrograms
.
push
(
program
);
}
else
{
cacheData
.
push
(
cache
);
}
});
});
if
(
filteredPrograms
.
length
)
{
promiseRequests
=
_
.
map
(
filteredPrograms
,
(
program
)
=>
{
return
[
programServiceHelper
.
getCollectionWithProgramId
(
program
,
req
),
programServiceHelper
.
getSampleContentWithProgramId
(
program
,
req
),
programServiceHelper
.
getContributionWithProgramId
(
program
,
req
),
programServiceHelper
.
getNominationWithProgramId
(
program
)];
});
forkJoin
(...
_
.
flatMapDeep
(
promiseRequests
)).
subscribe
((
responseData
)
=>
{
try
{
const
combainedRes
=
_
.
chunk
(
responseData
,
4
);
const
programDetailsArray
=
programServiceHelper
.
handleMultiProgramDetails
(
combainedRes
);
const
tableData
=
_
.
reduce
(
programDetailsArray
,
(
final
,
data
,
index
)
=>
{
final
.
push
({
program_id
:
filteredPrograms
[
index
],
values
:
data
});
return
final
;
},
[]);
_
.
forEach
(
tableData
,
(
obj
)
=>
{
cacheManager
.
set
({
key
:
`program_details_
${
obj
.
program_id
}
`
,
value
:
obj
},
function
(
err
,
cacheCSVData
)
{
if
(
err
)
{
logger
.
error
({
msg
:
'
Error - caching
'
,
err
,
additionalInfo
:
{
programDetails
:
obj
}},
req
)
}
else
{
logger
.
debug
({
msg
:
'
Caching nomination list - done
'
,
additionalInfo
:
{
nominationData
:
obj
}},
req
)
}
});
});
rspObj
.
result
=
{
tableData
:
[...
tableData
,
...
cacheData
]
}
rspObj
.
responseCode
=
'
OK
'
return
res
.
status
(
200
).
send
(
successResponse
(
rspObj
));
}
catch
(
err
)
{
loggerError
(
'
Error due to unhandled exception
'
,
rspObj
.
errCode
,
rspObj
.
errMsg
,
rspObj
.
responseCode
,
err
,
req
)
return
res
.
status
(
400
).
send
(
errorResponse
(
rspObj
));
}
},
(
err
)
=>
{
loggerError
(
'
Error http requests or nomination table query promise failure
'
,
rspObj
.
errCode
,
rspObj
.
errMsg
,
rspObj
.
responseCode
,
err
,
req
)
return
res
.
status
(
400
).
send
(
errorResponse
(
rspObj
));
});
}
else
{
rspObj
.
result
=
{
tableData
:
[...
cacheData
]
}
rspObj
.
responseCode
=
'
OK
'
return
res
.
status
(
200
).
send
(
successResponse
(
rspObj
));
}
}
function
aggregatedNominationCount
(
data
,
result
)
{
return
new
Promise
((
resolve
,
reject
)
=>
{
try
{
...
...
@@ -1310,3 +1369,4 @@ module.exports.healthAPI = health
module
.
exports
.
programCopyCollectionAPI
=
programCopyCollections
;
module
.
exports
.
getAllConfigurationsAPI
=
getAllConfigurations
;
module
.
exports
.
getConfigurationByKeyAPI
=
getConfigurationByKey
;
module
.
exports
.
downloadProgramDetailsAPI
=
downloadProgramDetails
This diff is collapsed.
Click to expand it.
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment
Menu
Explore
Projects
Groups
Topics
Snippets