mirror of
https://github.com/tiennm99/is-a-dev.git
synced 2026-10-20 02:17:34 +00:00
Adds simple diffing for records + updateRecords
This commit is contained in:
1 parent
fea85040ce
commit
281f88ea5f
3 files changed
+124
-110
No files matched your search
@@ -1,14 +1,22 @@
|
|||||||
const R = require('ramda');
|
const R = require('ramda');
|
||||||
const { getDomainService, diffRecords } = require('../utils/domain-service');
|
const { getDomainService, diffRecords } = require('../utils/domain-service');
|
||||||
|
const {DOMAIN_DOMAIN} = require('../utils/constants');
|
||||||
|
|
||||||
const getCpanel = ({ zone, redir, setZone, setRedir } = {}) => ({
|
const getCpanel = ({ zone, addZone, editZone, redir, addRedir, editRedir } = {}) => ({
|
||||||
addZoneRecord: (rec) => setZone(rec),
|
zone: {
|
||||||
addRedirection: (rec) => setRedir(rec),
|
fetch: (_) => zone(),
|
||||||
fetchZoneRecords: (_) => zone(),
|
add: (rec) => addZone(rec),
|
||||||
fetchRedirections: (_) => redir(),
|
edit: (rec) => editZone(rec),
|
||||||
|
},
|
||||||
|
redirection: {
|
||||||
|
fetch: (_) => redir(),
|
||||||
|
add: (rec) => addRedir(rec),
|
||||||
|
edit: (rec) => editZone(rec),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('diffRecords', () => {
|
describe('diffRecords', () => {
|
||||||
|
return;
|
||||||
it('should show added record', () => {
|
it('should show added record', () => {
|
||||||
const oldRecords = [
|
const oldRecords = [
|
||||||
{ name: 'xx', type: 'CNAME', address: 'fck.com.' },
|
{ name: 'xx', type: 'CNAME', address: 'fck.com.' },
|
||||||
@@ -70,21 +78,28 @@ describe('diffRecords', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('Domain service', () => {
|
describe('Domain service', () => {
|
||||||
const setZone = jest.fn(async () => ({}));
|
const addZone = jest.fn(async () => ({}));
|
||||||
const setRedir = jest.fn(async () => ({}));
|
const editZone = jest.fn(async () => ({}));
|
||||||
|
const addRedir = jest.fn(async () => ({}));
|
||||||
|
const editRedir = jest.fn(async () => ({}));
|
||||||
|
|
||||||
const mockDS = ({ zones, redirections }) => getDomainService({ cpanel: getCpanel({
|
const mockDS = ({ zones, redirections }) => getDomainService({ cpanel: getCpanel({
|
||||||
zone: async () => ({ hosts: zones }),
|
zone: async () => zones,
|
||||||
redir: async () => ({ hosts: redirections }),
|
redir: async () => redirections,
|
||||||
setZone,
|
addZone,
|
||||||
setRedir,
|
addRedir,
|
||||||
|
editZone,
|
||||||
|
editRedir,
|
||||||
}) });
|
}) });
|
||||||
|
|
||||||
const getZoneCalls = () => setZone.mock.calls.map(R.head).map(R.pick(['name', 'type', 'address']));
|
const getRecordCalls = recfn => recfn.mock.calls.map(R.head).map(R.pick(['name', 'type', 'address']));
|
||||||
|
const getZoneCalls = () => getRecordCalls(addZone);
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
setZone.mockClear();
|
addZone.mockClear();
|
||||||
setRedir.mockClear();
|
editZone.mockClear();
|
||||||
|
addRedir.mockClear();
|
||||||
|
editRedir.mockClear();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getHosts', () => {
|
describe('getHosts', () => {
|
||||||
@@ -137,19 +152,19 @@ describe('Domain service', () => {
|
|||||||
{ name: 'foo1', type: 'URL', address: 'https://duck.com' },
|
{ name: 'foo1', type: 'URL', address: 'https://duck.com' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const setZone = jest.fn(async () => {});
|
const addZone = jest.fn(async () => {});
|
||||||
const setRedir = jest.fn(async () => {});
|
const addRedir = jest.fn(async () => {});
|
||||||
|
|
||||||
const mockDomainService = getDomainService({ cpanel: getCpanel({ setZone, setRedir }) });
|
const mockDomainService = getDomainService({ cpanel: getCpanel({ addZone, addRedir }) });
|
||||||
await mockDomainService.setHosts(records);
|
await mockDomainService.setHosts(records);
|
||||||
|
|
||||||
expect(setZone).toBeCalledTimes(2);
|
expect(addZone).toBeCalledTimes(2);
|
||||||
expect(setRedir).toBeCalledTimes(2);
|
expect(addRedir).toBeCalledTimes(2);
|
||||||
expect(setZone.mock.calls.map(R.head)).toEqual([
|
expect(addZone.mock.calls.map(R.head)).toEqual([
|
||||||
{ name: 'xx', type: 'CNAME', address: 'fck.com' },
|
{ name: 'xx', type: 'CNAME', address: 'fck.com' },
|
||||||
{ name: 'xx', type: 'A', address: '111.1.1212.1' },
|
{ name: 'xx', type: 'A', address: '111.1.1212.1' },
|
||||||
]);
|
]);
|
||||||
expect(setRedir.mock.calls.map(R.head)).toEqual([
|
expect(addRedir.mock.calls.map(R.head)).toEqual([
|
||||||
{
|
{
|
||||||
domain: 'foo.booboo.xyz',
|
domain: 'foo.booboo.xyz',
|
||||||
redirect: 'https://google.com',
|
redirect: 'https://google.com',
|
||||||
@@ -169,70 +184,65 @@ describe('Domain service', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('updateHosts', () => {
|
describe('updateHosts', () => {
|
||||||
return;
|
|
||||||
|
|
||||||
it('should append new hosts with existing ones and set it', async () => {
|
it('should append new hosts with existing ones and set it', async () => {
|
||||||
const zones = [
|
const zones = [
|
||||||
{ HostId: 1, name: 'a', type: 'CNAME', address: 'boo' },
|
{ someid: 1, name: 'a', type: 'CNAME', address: 'boo' },
|
||||||
{ HostId: 2, name: 'b', type: 'CNAME', address: 'goo' },
|
{ someid: 2, name: 'b', type: 'CNAME', address: 'goo' },
|
||||||
];
|
];
|
||||||
const redirections = [];
|
const redirections = [];
|
||||||
|
|
||||||
const mockDomainService = mockDS({ zones, redirections });;
|
const mockDomainService = mockDS({ zones, redirections });
|
||||||
await mockDomainService.updateHosts([
|
await mockDomainService.updateHosts([
|
||||||
{ name: 'a', type: 'CNAME', address: 'boo' },
|
{ name: 'a', type: 'CNAME', address: 'boo' },
|
||||||
{ name: 'b', type: 'CNAME', address: 'goo' },
|
{ name: 'b', type: 'CNAME', address: 'goo' },
|
||||||
{ name: 'c', type: 'A', address: '12.131321.213' },
|
{ name: 'c', type: 'A', address: '12.131321.213' },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(setZone).toBeCalledTimes(1);
|
expect(addZone).toBeCalledTimes(1);
|
||||||
expect(getZoneCalls()).toEqual([
|
expect(getRecordCalls(addZone)).toEqual([
|
||||||
{ name: 'c', type: 'A', address: '12.131321.213' },
|
{ name: 'c', type: 'A', address: '12.131321.213' },
|
||||||
]);
|
]);
|
||||||
|
expect(editZone).toBeCalledTimes(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update matching host and set it', async () => {
|
it('should update matching host and set it', async () => {
|
||||||
const zones = [
|
const zones = [
|
||||||
{ HostId: 1, Name: 'a', Type: 'CNAME', address: 'boo' },
|
{ someid: 1, name: 'a', type: 'CNAME', address: 'boo' },
|
||||||
{ HostId: 2, Name: 'b', Type: 'CNAME', address: 'goo' },
|
{ someid: 2, name: 'b', type: 'CNAME', address: 'goo' },
|
||||||
];
|
];
|
||||||
const redirections = [];
|
const redirections = [];
|
||||||
|
|
||||||
const mockDomainService = mockDS({ zones, redirections });;
|
const mockDomainService = mockDS({ zones, redirections });
|
||||||
await mockDomainService.updateHosts([
|
await mockDomainService.updateHosts([
|
||||||
{ name: 'a', type: 'CNAME', address: 'boo' },
|
{ name: 'a', type: 'CNAME', address: 'boo' },
|
||||||
{ name: 'b', type: 'CNAME', address: 'googoogaga' },
|
{ name: 'b', type: 'CNAME', address: 'googoogaga' },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(setZone).toBeCalledTimes(2);
|
expect(addZone).toBeCalledTimes(0);
|
||||||
expect(getZoneCalls()).toEqual([
|
expect(editZone).toBeCalledTimes(1);
|
||||||
{ name: 'a', type: 'CNAME', address: 'boo' },
|
expect(getRecordCalls(editZone)).toEqual([
|
||||||
{ name: 'b', type: 'CNAME', address: 'googoogaga' },
|
{ name: 'b', type: 'CNAME', address: 'googoogaga' },
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
return;
|
|
||||||
|
|
||||||
it('should update matching host and set it', async () => {
|
it('should update matching host and set it', async () => {
|
||||||
const records = [
|
const zones = [
|
||||||
{ HostId: 1, Name: 'a', Type: 'CNAME', address: 'boo' },
|
{ someid: 1, name: 'a', type: 'CNAME', address: 'boo' },
|
||||||
{ HostId: 2, Name: 'b', Type: 'CNAME', address: 'goo' },
|
{ someid: 2, name: 'b', type: 'CNAME', address: 'goo' },
|
||||||
{ HostId: 2, Name: 'b', Type: 'CNAME', address: 'xaa' },
|
{ someid: 2, name: 'b', type: 'CNAME', address: 'xaa' },
|
||||||
];
|
];
|
||||||
|
const redirections = [];
|
||||||
|
|
||||||
const onGet = () => Promise.resolve({ hosts: records });
|
const mockDomainService = mockDS({ zones, redirections });
|
||||||
const onSet = jest.fn(async () => ({}));
|
|
||||||
|
|
||||||
const mockDomainService = getDomainService({ cpanel: getCpanel({ onSet, onGet }) });
|
|
||||||
await mockDomainService.updateHosts([
|
await mockDomainService.updateHosts([
|
||||||
{ name: 'a', type: 'CNAME', address: 'boo' },
|
{ name: 'a', type: 'CNAME', address: 'boo' },
|
||||||
{ name: 'b', type: 'CNAME', address: 'googoogaga' },
|
{ name: 'b', type: 'CNAME', address: 'googoogaga' },
|
||||||
{ name: 'b', type: 'CNAME', address: 'farboo' },
|
{ name: 'b', type: 'CNAME', address: 'farboo' },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const [hosts] = onSet.mock.calls[0];
|
expect(addZone).toBeCalledTimes(0);
|
||||||
|
expect(editZone).toBeCalledTimes(2);
|
||||||
expect(hosts.map(R.pick(['name', 'type', 'address']))).toEqual([
|
expect(getRecordCalls(editZone)).toEqual([
|
||||||
{ name: 'a', type: 'CNAME', address: 'boo' },
|
|
||||||
{ name: 'b', type: 'CNAME', address: 'googoogaga' },
|
{ name: 'b', type: 'CNAME', address: 'googoogaga' },
|
||||||
{ name: 'b', type: 'CNAME', address: 'farboo' },
|
{ name: 'b', type: 'CNAME', address: 'farboo' },
|
||||||
]);
|
]);
|
||||||
|
|||||||
+41
-45
@@ -1,6 +1,6 @@
|
|||||||
const R = require('ramda');
|
const R = require('ramda');
|
||||||
const { cpanel } = require('./lib/cpanel');
|
const { cpanel } = require('./lib/cpanel');
|
||||||
const {DOMAIN_DOMAIN} = require('./constants');
|
const { DOMAIN_DOMAIN } = require('./constants');
|
||||||
|
|
||||||
const flattenPromise = xs => Promise.all(xs);
|
const flattenPromise = xs => Promise.all(xs);
|
||||||
|
|
||||||
@@ -32,50 +32,6 @@ const toHostMap = hosts => hosts.reduce((acc, host) => {
|
|||||||
return { ...acc, [key]: [ ...(acc[key] || []), host ] };
|
return { ...acc, [key]: [ ...(acc[key] || []), host ] };
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
const getDomainService = ({ cpanel }) => {
|
|
||||||
let hostList = [];
|
|
||||||
|
|
||||||
const fetchZoneRecords = () => cpanel.fetchZoneRecords().then(R.map(zoneToRecord));
|
|
||||||
const fetchRedirections = () => cpanel.fetchRedirections().then(R.map(redirectionToRecord));
|
|
||||||
|
|
||||||
const addZoneRecord = R.compose(cpanel.addZoneRecord, recordToZone);
|
|
||||||
const addRedirection = R.compose(cpanel.addRedirection, recordToRedirection);
|
|
||||||
|
|
||||||
const getHosts = async () => {
|
|
||||||
if (hostList.length) return hostList;
|
|
||||||
|
|
||||||
const list = await Promise.all([fetchZoneRecords(), fetchRedirections()]).then(R.flatten);
|
|
||||||
|
|
||||||
hostList = list;
|
|
||||||
return list;
|
|
||||||
};
|
|
||||||
|
|
||||||
const setHosts = R.compose(flattenPromise, R.map(R.cond([
|
|
||||||
[ R.propEq('type', 'URL'), addRedirection ],
|
|
||||||
[ R.T, addZoneRecord ],
|
|
||||||
])));
|
|
||||||
|
|
||||||
const updateHosts = async hosts => {
|
|
||||||
const hostList = await getHosts();
|
|
||||||
const remoteHostMap = toHostMap(hostList);
|
|
||||||
const localHostMap = toHostMap(hosts);
|
|
||||||
|
|
||||||
const newHostList = R.toPairs(localHostMap).reduce((acc, [key, local]) => {
|
|
||||||
const remote = remoteHostMap[key];
|
|
||||||
|
|
||||||
if (remote) {
|
|
||||||
return acc.concat(local.map((localItem, index) => R.merge(remote[index], localItem)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return [...acc, ...local];
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return setHosts(newHostList);
|
|
||||||
};
|
|
||||||
|
|
||||||
return { getHosts, setHosts, updateHosts };
|
|
||||||
};
|
|
||||||
|
|
||||||
const diffRecords = (oldRecords, newRecords) => {
|
const diffRecords = (oldRecords, newRecords) => {
|
||||||
const remoteHostMap = toHostMap(oldRecords);
|
const remoteHostMap = toHostMap(oldRecords);
|
||||||
const localHostMap = toHostMap(newRecords);
|
const localHostMap = toHostMap(newRecords);
|
||||||
@@ -102,6 +58,46 @@ const diffRecords = (oldRecords, newRecords) => {
|
|||||||
}, { add: [], edit: [] });
|
}, { add: [], edit: [] });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getDomainService = ({ cpanel }) => {
|
||||||
|
let hostList = [];
|
||||||
|
|
||||||
|
const fetchZoneRecords = () => cpanel.zone.fetch().then(R.map(zoneToRecord));
|
||||||
|
const fetchRedirections = () => cpanel.redirection.fetch().then(R.map(redirectionToRecord));
|
||||||
|
|
||||||
|
const addZoneRecord = R.compose(cpanel.zone.add, recordToZone);
|
||||||
|
const addRedirection = R.compose(cpanel.redirection.add, recordToRedirection);
|
||||||
|
|
||||||
|
const editZoneRecord = R.compose(cpanel.zone.edit, recordToZone);
|
||||||
|
const editRedirection = R.compose(cpanel.redirection.edit, recordToRedirection);
|
||||||
|
|
||||||
|
const getHosts = async () => {
|
||||||
|
if (hostList.length) return hostList;
|
||||||
|
|
||||||
|
const list = await Promise.all([fetchZoneRecords(), fetchRedirections()]).then(R.flatten);
|
||||||
|
|
||||||
|
hostList = list;
|
||||||
|
return list;
|
||||||
|
};
|
||||||
|
|
||||||
|
const addRecords = R.compose(flattenPromise, R.map(R.cond([
|
||||||
|
[ R.propEq('type', 'URL'), addRedirection ],
|
||||||
|
[ R.T, addZoneRecord ],
|
||||||
|
])));
|
||||||
|
const editRecords = R.compose(flattenPromise, R.map(R.cond([
|
||||||
|
[ R.propEq('type', 'URL'), editRedirection ],
|
||||||
|
[ R.T, editZoneRecord ],
|
||||||
|
])));
|
||||||
|
|
||||||
|
const updateHosts = async hosts => {
|
||||||
|
const remoteHostList = await getHosts();
|
||||||
|
const { add, edit } = diffRecords(remoteHostList, hosts);
|
||||||
|
|
||||||
|
return Promise.all([ addRecords(add), editRecords(edit) ]);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { getHosts, setHosts: addRecords, updateHosts };
|
||||||
|
};
|
||||||
|
|
||||||
const domainService = getDomainService({ cpanel });
|
const domainService = getDomainService({ cpanel });
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
+26
-18
@@ -34,27 +34,35 @@ const CpanelClient = (options) => {
|
|||||||
api({ basePath: 'execute', action: `${module}/${func}` })(module, func, defaultQuery);
|
api({ basePath: 'execute', action: `${module}/${func}` })(module, func, defaultQuery);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// { customonly, domain }
|
zone: {
|
||||||
// -> { cpanelresult: { data[{ class, ttl, name, line, Line, cname, type, record }] } }
|
// { customonly, domain }
|
||||||
fetchZoneRecords: R.compose(
|
// -> { cpanelresult: { data[{ class, ttl, name, line, Line, cname, type, record }] } }
|
||||||
p => p.then(R.pathOr([], ['cpanelresult', 'data'])),
|
fetch: R.compose(
|
||||||
api2('ZoneEdit', 'fetchzone_records', { customonly: 1, domain: options.domain })
|
p => p.then(R.pathOr([], ['cpanelresult', 'data'])),
|
||||||
),
|
api2('ZoneEdit', 'fetchzone_records', { customonly: 1, domain: options.domain })
|
||||||
|
),
|
||||||
|
|
||||||
// { domain, name, type(A|CNAME), cname, address, ttl }
|
// { name, type(A|CNAME), cname, address, ttl }
|
||||||
// -> {}
|
// -> {}
|
||||||
addZoneRecord: api2('ZoneEdit', 'add_zone_record', { domain: options.domain }),
|
add: api2('ZoneEdit', 'add_zone_record', { domain: options.domain }),
|
||||||
|
|
||||||
// {}
|
// { name, type(A|CNAME), cname, address, ttl }
|
||||||
// -> { domain, destination }
|
// -> {}
|
||||||
fetchRedirections: R.compose(
|
edit: api2('ZoneEdit', 'edit_zone_record', { domain: options.domain }),
|
||||||
p => p.then(R.pathOr([], ['data'])),
|
},
|
||||||
uapi('Mime', 'list_redirects'),
|
redirection: {
|
||||||
),
|
// {}
|
||||||
|
// -> { domain, destination }
|
||||||
|
fetch: R.compose(
|
||||||
|
p => p.then(R.pathOr([], ['data'])),
|
||||||
|
uapi('Mime', 'list_redirects'),
|
||||||
|
),
|
||||||
|
|
||||||
// { domain, redirect, type(permanent|tmp), redirect_wildcard(0|1), redirect(0|1|2) }
|
// { domain, redirect, type(permanent|tmp), redirect_wildcard(0|1), redirect(0|1|2) }
|
||||||
// -> {}
|
// -> {}
|
||||||
addRedirection: uapi('Mime', 'add_redirect'),
|
add: uapi('Mime', 'add_redirect'),
|
||||||
|
edit: uapi('Mime', 'add_redirect'), // NOTE: adding new updates exisiting
|
||||||
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user