mirror of
https://github.com/LukeHagar/api-specs.git
synced 2025-12-06 20:37:44 +00:00
418 lines
15 KiB
JavaScript
418 lines
15 KiB
JavaScript
const fs = require('fs')
|
|
const args = process.argv;
|
|
const pmConvert = require('./PostmanCovertions')
|
|
const pmAPI = require('./postmanAPI')
|
|
let privateRemoteCollectionId = '23226990-e27d8a83-24d7-4de8-9c33-419fe65aaa9c' //must be in normal ID format
|
|
let mainPublicCollectionId = '23836355-220ca56f-ba1b-4ff2-ad1e-7e0a6bfb6cb4' //must be in UUID format
|
|
let localCollection = {}//JSON.parse(fs.readFileSync(`C:\\git\\api-specs\\postman\\collections\\sailpoint-api-v3.json`).toString())
|
|
let changesMade = false;
|
|
|
|
const postmanCollections = {
|
|
v3: 'daf693e6-7856-4c62-8c11-4102e6729766',
|
|
v3Uid: '23226990-daf693e6-7856-4c62-8c11-4102e6729766',
|
|
v3Public: '23226990-3721beea-5615-44b4-9459-e858a0ca7aed',
|
|
v3Location: 'postman/collections/sailpoint-api-v3.json',
|
|
v3SpecLocation: 'dereferenced/deref-sailpoint-api.v3.json',
|
|
beta: 'a5545e8a-7941-4518-8763-4b09adcee185',
|
|
betaUid: '23226990-a5545e8a-7941-4518-8763-4b09adcee185',
|
|
betaPublic: '23226990-3b87172a-cd55-40a2-9ace-1560a1158a4e',
|
|
betaLocation: 'postman/collections/sailpoint-api-beta.json',
|
|
betaSpecLocation: 'dereferenced/deref-sailpoint-api.beta.json',
|
|
nerm: '91b47f89-5fc4-4111-b9c6-382cf29d1475',
|
|
nermUid: '23226990-91b47f89-5fc4-4111-b9c6-382cf29d1475',
|
|
nermPublic: '23226990-20d718e3-b9b3-43ad-850c-637b00864ae2',
|
|
nermLocation: 'postman/collections/sailpoint-api-nerm.json'
|
|
|
|
}
|
|
|
|
|
|
const release = async () => {
|
|
|
|
|
|
privateRemoteCollectionId = postmanCollections[args[2].toLowerCase()]
|
|
privateRemoteCollectionIdUid = postmanCollections[args[2].toLowerCase() + 'Uid']
|
|
mainPublicCollectionId = postmanCollections[args[2].toLowerCase() + 'Public']
|
|
localCollection = JSON.parse(fs.readFileSync(postmanCollections[args[2].toLowerCase() + 'Location'], 'utf8'))
|
|
//SpecCollection = JSON.parse(fs.readFileSync(postmanCollections[args[2].toLowerCase() + 'SpecLocation'], 'utf8'))
|
|
|
|
|
|
let remoteCollection = await refreshRemoteCollection(privateRemoteCollectionId)
|
|
|
|
|
|
|
|
|
|
// This step just cleans up the variables so they match what is returned in postman
|
|
for (let variable of localCollection.variable) {
|
|
if (variable.type) {
|
|
delete variable.type
|
|
}
|
|
}
|
|
|
|
// check if the top level collection has changed, If so, update it... This will delete all folders unfortunately.
|
|
// skipping this for now as it's just too dangerous and we can manually update in these cases
|
|
|
|
if (1===2) {
|
|
|
|
if (checkIfDifferent(removeIdFields(localCollection.event), removeIdFields(remoteCollection.collection.event))
|
|
|| checkIfDifferent( removeIdFields(localCollection.info.description.content), removeIdFields(remoteCollection.collection.info.description))
|
|
|| checkIfDifferent( removeIdFields(localCollection.info.name), removeIdFields(remoteCollection.collection.info.name))
|
|
|| checkIfDifferent(removeIdFields(localCollection.variable), removeIdFields(remoteCollection.collection.variable))
|
|
|| checkIfDifferent( removeIdFields(localCollection.auth), removeIdFields(remoteCollection.collection.auth))) {
|
|
const localEmptyCollection = { ...localCollection }
|
|
localEmptyCollection.item = []
|
|
//delete all folders. Do this one at a time so it doesn't timeout
|
|
for (let item of remoteCollection.collection.item) {
|
|
await new pmAPI.Folder(privateRemoteCollectionId).delete(item.id)
|
|
changesMade = true
|
|
}
|
|
// now update the collection with the changes. All folders will be added later in code
|
|
let updatedCollection = await new pmAPI.Collection(privateRemoteCollectionId).update({collection : localEmptyCollection})
|
|
remoteCollection = await refreshRemoteCollection(privateRemoteCollectionId)
|
|
}
|
|
}
|
|
|
|
|
|
// add any missing folders
|
|
for (let item of localCollection.item) {
|
|
let folder = getMatchingFolder(item, remoteCollection.collection.item)
|
|
if (folder == null) {
|
|
await updateEntireFolder(item)
|
|
} else {
|
|
if (checkIfDifferent(folder.description, item.description)) {
|
|
console.log(`updating folder ${folder.name}`)
|
|
await new pmAPI.Folder(privateRemoteCollectionId).update(folder.id, { description: item.description })
|
|
changesMade = true
|
|
console.log(`updated folder ${folder.name}`)
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
// delete any folders that are no longer in the collection
|
|
for (let folder of remoteCollection.collection.item) {
|
|
let localFolder = getMatchingFolder(folder, localCollection.item)
|
|
if (localFolder == null) {
|
|
await new pmAPI.Folder(privateRemoteCollectionId).delete(folder.id)
|
|
changesMade = true
|
|
}
|
|
}
|
|
|
|
// delete any requests that are no longer in the collection
|
|
for (let folder of remoteCollection.collection.item) {
|
|
let localFolder = getMatchingFolder(folder, localCollection.item)
|
|
for (let items of folder.item) {
|
|
let remoteRequest = getMatchingRequest(items, localFolder.item)
|
|
if (remoteRequest == null) {
|
|
await new pmAPI.Request(privateRemoteCollectionId).delete(items.id)
|
|
changesMade = true
|
|
}
|
|
}
|
|
}
|
|
|
|
// update any requests that have changed
|
|
for (let item of localCollection.item) {
|
|
let folder = getMatchingFolder(item, remoteCollection.collection.item)
|
|
if (folder !== null) {
|
|
await updateRequestsInFolder(item.item, folder.id, folder)
|
|
}
|
|
}
|
|
|
|
// push changes to the forked collections
|
|
if (changesMade) {
|
|
const msg = 'Merging to public collection'
|
|
console.log('\n' + msg + '...')
|
|
await new pmAPI.Collection(privateRemoteCollectionIdUid).merge(mainPublicCollectionId)
|
|
.then(() => {
|
|
console.log(msg, '-> OK\n') }
|
|
)
|
|
.catch((error) => {
|
|
console.log(msg, '-> FAIL')
|
|
throw error("Failed to merge to public collection")
|
|
})
|
|
} else {
|
|
console.log('No changes made')
|
|
}
|
|
|
|
|
|
}
|
|
|
|
function getMatchingFolder(localFolder, remoteFolders) {
|
|
for (let folder of remoteFolders) {
|
|
if (folder.name === localFolder.name) {
|
|
return folder
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
|
|
function getMatchingRequest(localRequest, remoteRequests) {
|
|
for (let request of remoteRequests) {
|
|
if (request.name === localRequest.name && request.request.method === localRequest.request.method && localRequest.request.url.host + '/' + localRequest.request.url.path.join('/') === request.request.url.host + '/' + request.request.url.path.join('/')) {
|
|
return request
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
|
|
function getMatchingResponse(localResponse, remoteResponses) {
|
|
for (let response of remoteResponses) {
|
|
if (response.name === localResponse.name && response.responseCode.code === localResponse.responseCode.code) {
|
|
return response
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
|
|
function buildRequestBody(items) {
|
|
if (items === null) {
|
|
return null
|
|
}
|
|
|
|
let responses = []
|
|
for (let response of items.response) {
|
|
responses.push(pmConvert.responseFromLocal(response, {}))
|
|
}
|
|
let postmanRequestBody = pmConvert.requestFromLocal(items, responses)
|
|
return postmanRequestBody
|
|
}
|
|
|
|
function checkIfDifferent(source, dest) {
|
|
syncKeys(source, dest)
|
|
if (isDeepEqual(source, dest)) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
function removeIdFields(obj) {
|
|
if (typeof obj !== 'object') {
|
|
return
|
|
}
|
|
if (obj === null) {
|
|
return
|
|
}
|
|
// Check if the current object has the 'id' property
|
|
if (obj.hasOwnProperty('id')) {
|
|
delete obj.id;
|
|
}
|
|
|
|
// Recursively call removeIdFields on each property if it's an object
|
|
for (let key in obj) {
|
|
if (typeof obj[key] === 'object' && obj[key] !== null) {
|
|
removeIdFields(obj[key]);
|
|
}
|
|
}
|
|
}
|
|
|
|
function isNullorEmpty(obj) {
|
|
if (obj === null || obj === '' || obj === undefined) {
|
|
return true
|
|
}
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
function syncKeys(obj1, obj2) {
|
|
if (typeof obj1 !== 'object' || typeof obj2 !== 'object' || obj1 == null || obj2 == null) {
|
|
return;
|
|
}
|
|
|
|
const keys1 = Object.keys(obj1);
|
|
const keys2 = Object.keys(obj2);
|
|
|
|
for (let key of keys1) {
|
|
const val1 = obj1[key];
|
|
const val2 = obj2[key];
|
|
if (val2 === undefined) {
|
|
continue;
|
|
} else if (key === 'id') {
|
|
obj1[key] = val2;
|
|
}
|
|
const areObjects = isObject(val1) && isObject(val2);
|
|
if (areObjects) {
|
|
syncKeys(val1, val2)
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
function isDeepEqual(obj1, obj2) {
|
|
|
|
if (areValuesEqual(obj1, obj2)) {
|
|
return true
|
|
}
|
|
|
|
if (typeof obj1 !== 'object' || typeof obj2 !== 'object' || obj1 == null || obj2 == null) {
|
|
return false;
|
|
}
|
|
|
|
const keys1 = Object.keys(obj1);
|
|
const keys2 = Object.keys(obj2);
|
|
|
|
if (keys1.length !== keys2.length) {
|
|
return false;
|
|
}
|
|
|
|
for (let key of keys1) {
|
|
let val1 = findJSONObjects(obj1[key]);
|
|
let val2 = findJSONObjects(obj2[key]);
|
|
if (shouldIgnore(key)) {
|
|
continue;
|
|
}
|
|
const areObjects = isObject(val1) && isObject(val2);
|
|
if (
|
|
areObjects && !isDeepEqual(val1, val2) ||
|
|
(!areObjects && !areValuesEqual(val1, val2))
|
|
) {
|
|
console.log(`found difference in ${key} value1: ${val1} value2: ${val2}`)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function shouldIgnore(key) {
|
|
return false
|
|
}
|
|
|
|
function findJSONObjects(obj) {
|
|
const jsonObjects = {};
|
|
|
|
if (typeof obj === 'string') {
|
|
try {
|
|
// Attempt to parse the string as JSON
|
|
const parsed = JSON.parse(obj);
|
|
|
|
// Check if the parsed result is an object (and not a number, string, etc.)
|
|
if (parsed !== null && typeof parsed === 'object') {
|
|
return parsed;
|
|
}
|
|
} catch (e) {
|
|
return obj;
|
|
}
|
|
}
|
|
|
|
return obj;
|
|
}
|
|
|
|
function areValuesEqual(val1, val2) {
|
|
if (isNullorEmpty(val1) && isNullorEmpty(val2)) {
|
|
return true;
|
|
}
|
|
if (val1 === val2) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
function isObject(object) {
|
|
return object != null && typeof object === 'object';
|
|
}
|
|
|
|
|
|
async function updateEntireFolder(item, folderId) {
|
|
if (item.item && !folderId) {
|
|
let newFolder = await new pmAPI.Folder(privateRemoteCollectionId).create(item)
|
|
await updateEntireFolder(item.item, newFolder.data.id)
|
|
} else {
|
|
for (let items of item) {
|
|
let responses = []
|
|
for (let response of items.response) {
|
|
responses.push(pmConvert.responseFromLocal(response, {}))
|
|
}
|
|
let postmanRequestBody = pmConvert.requestFromLocal(items, responses)
|
|
console.log(`creating request ${postmanRequestBody.name} with id: ${postmanRequestBody.id}`)
|
|
let newRequest = await new pmAPI.Request(privateRemoteCollectionId).create(postmanRequestBody, folderId)
|
|
changesMade = true
|
|
console.log(newRequest.data.name)
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
async function updateRequestsInFolder(item, folderId, remoteItem) {
|
|
for (let items of item) {
|
|
let remoteRequest = getMatchingRequest(items, remoteItem.item)
|
|
let postmanRequestBody = buildRequestBody(items)
|
|
let remotePostmanBody = buildRequestBody(remoteRequest)
|
|
if (remoteRequest !== null) {
|
|
// remove responses from request that are no longer there
|
|
for (let response of remotePostmanBody.responses) {
|
|
let localResponse = getMatchingResponse(response, postmanRequestBody.responses)
|
|
if (localResponse == null) {
|
|
console.log(`response no longer exists, deleting response ${response.name}`)
|
|
let newRequestDelete = await new pmAPI.Response(privateRemoteCollectionId).delete(response.id)
|
|
changesMade = true
|
|
//console.log(`deleted response ${newRequestDelete.data.id}`)
|
|
}
|
|
}
|
|
|
|
|
|
// check all responses in request
|
|
for (let response of postmanRequestBody.responses) {
|
|
let remoteResponse = getMatchingResponse(response, remotePostmanBody.responses)
|
|
if (checkIfDifferent(response, remoteResponse)) {
|
|
if (remoteResponse) {
|
|
let newRequestDelete = await new pmAPI.Response(privateRemoteCollectionId).update(response, remoteResponse.id)
|
|
console.log(`change found, updated response ${newRequestDelete.data.id} in request ${response.name}`)
|
|
changesMade = true
|
|
} else {
|
|
let newRequest = await new pmAPI.Response(privateRemoteCollectionId).create(response, remoteRequest.id)
|
|
console.log(`response doesn't exist in ${response.name}, created response ${newRequest.data.name}`)
|
|
changesMade = true
|
|
}
|
|
} else {
|
|
//console.log(`no changes to request ${remoteRequest.name} response ${response.name}`)
|
|
}
|
|
}
|
|
delete remotePostmanBody.responses
|
|
delete postmanRequestBody.responses
|
|
} else {
|
|
console.log(`request is null`)
|
|
}
|
|
|
|
|
|
|
|
|
|
// now check if the request has any changes in it
|
|
if (checkIfDifferent(postmanRequestBody, remotePostmanBody)) {
|
|
postmanRequestBody = buildRequestBody(items)
|
|
if (remoteRequest) {
|
|
console.log(`updating request ${remoteRequest.name}`)
|
|
postmanRequestBody.folder = folderId
|
|
postmanRequestBody.collection = privateRemoteCollectionId
|
|
let newRequestDelete = await new pmAPI.Request(privateRemoteCollectionId).update(remoteRequest.id, postmanRequestBody)
|
|
changesMade = true
|
|
console.log(`updated request ${newRequestDelete.data.id}`)
|
|
} else {
|
|
let newRequest = await new pmAPI.Request(privateRemoteCollectionId).create(postmanRequestBody, folderId)
|
|
changesMade = true
|
|
console.log(`creating request ${newRequest.data.name}`)
|
|
}
|
|
} else {
|
|
console.log(`no changes to request ${remoteRequest.name}`)
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
async function refreshRemoteCollection (remoteCollectionID) {
|
|
const msg = 'Refreshing remote collection'
|
|
console.log('\n' + msg + '...\n')
|
|
const remoteCollection = await new pmAPI.Collection(remoteCollectionID).get()
|
|
.catch((error) => {
|
|
console.log(msg, '-> FAIL')
|
|
handlePostmanAPIError(error)
|
|
})
|
|
return remoteCollection
|
|
}
|
|
|
|
|
|
|
|
release() |