Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions bump-version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// deno run --allow-read --allow-write bump-version.ts [major|minor|patch]

let [bumpType] = Deno.args;
if (!bumpType) {
const answer = prompt('No argument provided. Default to patch? [Y/n]')?.trim().toLowerCase();
if (answer === '' || answer === 'y' || answer === 'yes') {
bumpType = 'patch';
console.log('Defaulting to patch.');
} else {
console.log('Aborted.');
Deno.exit(0);
}
}
if (!['major', 'minor', 'patch'].includes(bumpType)) {
console.error('Usage: deno task bump [major|minor|patch]');
Deno.exit(1);
}

const denoJsonPath = './deno.json';
const denoJsonRaw = await Deno.readTextFile(denoJsonPath);
const denoJson = JSON.parse(denoJsonRaw);

if (!denoJson.version) {
console.error('No version key found in deno.json');
Deno.exit(1);
}

const versionParts = denoJson.version.split('.').map(Number);
if (versionParts.length !== 3 || versionParts.some(isNaN)) {
console.error('Invalid version format in deno.json. Expected format: x.y.z');
Deno.exit(1);
}

const oldVersion = denoJson.version;
let [major, minor, patch] = versionParts;

switch (bumpType) {
case 'major':
major++;
minor = 0;
patch = 0;
break;
case 'minor':
minor++;
patch = 0;
break;
case 'patch':
patch++;
break;
}

const newVersion = `${major}.${minor}.${patch}`;
denoJson.version = newVersion;

await Deno.writeTextFile(denoJsonPath, JSON.stringify(denoJson, null, 2) + '\n');
console.log(`Bumped version from ${oldVersion} to ${newVersion}`);
5 changes: 3 additions & 2 deletions deno.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@johanfive/xmas",
"version": "0.0.2",
"version": "0.0.3",
"exports": "./src/index.ts",
"license": "MIT",
"imports": {
Expand All @@ -11,7 +11,8 @@
"tasks": {
"cache": "DENO_TLS_CA_STORE=system deno cache --reload src/**/*.ts",
"sandbox": "DENO_TLS_CA_STORE=system deno run --allow-read --allow-net --env-file=sandbox/.env --allow-env sandbox/index.ts",
"sandbox:validate-docs": "DENO_TLS_CA_STORE=system deno run --allow-read --allow-net --env-file=sandbox/.env --allow-env sandbox/validate-docs.ts"
"sandbox:validate-docs": "DENO_TLS_CA_STORE=system deno run --allow-read --allow-net --env-file=sandbox/.env --allow-env sandbox/validate-docs.ts",
"bump": "deno run --allow-read --allow-write bump-version.ts"
},
"fmt": {
"singleQuote": true,
Expand Down
39 changes: 39 additions & 0 deletions sandbox/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,50 @@ async function testPreExistingOAuthTokens() {
}
}

async function testGroupsSupervisors() {
console.log('\n=== Scenario 5: Test groups.getSupervisors ===');
const { hostname, username, password } = config.basicAuth;
if (!hostname || !username || !password) {
console.warn(
'[WARNING] groups.getSupervisors: Skipped (missing hostname, username, or password)',
);
return;
}
try {
const xm = new XmApi(config.basicAuth);
// First, get a list of groups to find one we can test with
const groupsResponse = await xm.groups.get({ query: { limit: 1 } });
console.log(
'[INFO] groups.getSupervisors: Found groups:',
groupsResponse.status,
groupsResponse.body,
);

if (groupsResponse.body.data && groupsResponse.body.data.length > 0) {
const firstGroup = groupsResponse.body.data[0];
const groupId = firstGroup.id || firstGroup.targetName;
console.log(`[INFO] groups.getSupervisors: Testing with group: ${groupId}`);

const supervisorsResponse = await xm.groups.getSupervisors(groupId, { query: { limit: 5 } });
console.log(
'[SUCCESS] groups.getSupervisors:',
supervisorsResponse.status,
supervisorsResponse.body,
);
} else {
console.log('[INFO] groups.getSupervisors: No groups found to test with');
}
} catch (err) {
printError('[ERROR] groups.getSupervisors:', err);
}
}

// Run all scenarios sequentially
await testBasicAuthOnly();
await testOauthViaBasicAuthWithExplicitClientId();
await testPasswordGrantWithDiscovery();
await testPreExistingOAuthTokens();
await testGroupsSupervisors();

function printError(context: string, err: unknown) {
if (err instanceof Error) {
Expand Down
98 changes: 98 additions & 0 deletions src/endpoints/groups/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ const mockSingleGroupBody = {
created: '2025-01-01T00:00:00.000Z',
};

const mockSinglePersonBody = {
id: 'test-person-id',
targetName: 'jdoe',
firstName: 'John',
lastName: 'Doe',
recipientType: 'PERSON',
status: 'ACTIVE',
created: '2025-01-01T00:00:00.000Z',
};

const mockPaginatedGroupsBody = {
count: 1,
total: 1,
Expand All @@ -32,6 +42,15 @@ const mockPaginatedGroupsBody = {
},
};

const mockPaginatedPeopleBody = {
count: 1,
total: 1,
data: [mockSinglePersonBody],
links: {
self: '/api/xm/1/groups/test-group-id/supervisors?limit=100&offset=0',
},
};

Deno.test('GroupsEndpoint', async (t) => {
await t.step('get() - List Groups', async (t) => {
await t.step('makes GET request without parameters', async () => {
Expand Down Expand Up @@ -354,6 +373,85 @@ Deno.test('GroupsEndpoint', async (t) => {
});
});

await t.step('getSupervisors() - Get Group Supervisors', async (t) => {
await t.step('makes GET request with group ID', async () => {
mockHttpClient.setReqRes([{
expectedRequest: {
method: 'GET',
url: 'https://test.xmatters.com/api/xm/1/groups/test-group-id/supervisors',
headers: TestConstants.BASIC_AUTH_HEADERS,
},
mockedResponse: {
status: 200,
headers: { 'content-type': 'application/json' },
body: mockPaginatedPeopleBody,
},
}]);
await groups.getSupervisors('test-group-id');
});

await t.step('makes GET request with group targetName', async () => {
mockHttpClient.setReqRes([{
expectedRequest: {
method: 'GET',
url: 'https://test.xmatters.com/api/xm/1/groups/Oracle Administrators/supervisors',
headers: TestConstants.BASIC_AUTH_HEADERS,
},
mockedResponse: {
status: 200,
headers: { 'content-type': 'application/json' },
body: mockPaginatedPeopleBody,
},
}]);
await groups.getSupervisors('Oracle Administrators');
});

await t.step('makes GET request with custom headers', async () => {
mockHttpClient.setReqRes([{
expectedRequest: {
method: 'GET',
url: 'https://test.xmatters.com/api/xm/1/groups/test-group-id/supervisors',
headers: {
...TestConstants.BASIC_AUTH_HEADERS,
'X-Custom-Header': 'custom-value',
},
},
mockedResponse: {
status: 200,
headers: { 'content-type': 'application/json' },
body: mockPaginatedPeopleBody,
},
}]);
await groups.getSupervisors('test-group-id', {
headers: {
'X-Custom-Header': 'custom-value',
},
});
});

await t.step('makes GET request with query parameters', async () => {
mockHttpClient.setReqRes([{
expectedRequest: {
method: 'GET',
url:
'https://test.xmatters.com/api/xm/1/groups/test-group-id/supervisors?limit=5&offset=10',
headers: TestConstants.BASIC_AUTH_HEADERS,
},
mockedResponse: {
status: 200,
headers: { 'content-type': 'application/json' },
body: mockPaginatedPeopleBody,
},
}]);
await groups.getSupervisors('test-group-id', {
query: {
limit: 5,
offset: 10,
},
});
});
});

await t.step('delete() - Delete Group', async (t) => {
await t.step('makes DELETE request with group ID', async () => {
mockHttpClient.setReqRes([{
Expand Down
Loading