fix(coreflow): Fix Coreflow identity lookup and response shape; improve API client tests and bump dependencies
This commit is contained in:
@@ -1,5 +1,14 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-08-18 - 5.0.5 - fix(coreflow)
|
||||||
|
Fix Coreflow identity lookup and response shape; improve API client tests and bump dependencies
|
||||||
|
|
||||||
|
- ts/manager.coreflow/coreflowmanager.ts: Use $elemMatch to correctly query nested user.tokens when resolving identities and validate machine user types.
|
||||||
|
- ts/manager.coreflow/coreflowmanager.ts: Normalize getClusterConfig response to include services (was deploymentDirectives) and tidy handler signatures.
|
||||||
|
- test/test.apiclient.ts: Add detailed logging and improved error handling across preTask, client startup, identity retrieval, image creation and image upload to aid debugging and test observability.
|
||||||
|
- package.json: Update dependency versions (notable bumps): @types/node -> ^22.0.0, @push.rocks/smartacme -> ^8.0.0, @push.rocks/smartdata -> ^5.16.4, @push.rocks/smartexpect -> ^2.5.0, @push.rocks/smartpath -> ^6.0.0, @push.rocks/smartrequest -> ^4.2.2, plus other maintenance bumps.
|
||||||
|
- Add .claude/settings.local.json to provide local Claude permissions for developer tooling.
|
||||||
|
|
||||||
## 2025-04-25 - 5.0.4 - fix(platformservice/mta)
|
## 2025-04-25 - 5.0.4 - fix(platformservice/mta)
|
||||||
Update getEmailStatus response schema: make details property optional
|
Update getEmailStatus response schema: make details property optional
|
||||||
|
|
||||||
|
12
package.json
12
package.json
@@ -28,7 +28,7 @@
|
|||||||
"@git.zone/tspublish": "^1.10.3",
|
"@git.zone/tspublish": "^1.10.3",
|
||||||
"@git.zone/tstest": "^2.3.5",
|
"@git.zone/tstest": "^2.3.5",
|
||||||
"@git.zone/tswatch": "^2.2.1",
|
"@git.zone/tswatch": "^2.2.1",
|
||||||
"@types/node": "^24.3.0"
|
"@types/node": "^22.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@api.global/typedrequest": "3.1.10",
|
"@api.global/typedrequest": "3.1.10",
|
||||||
@@ -47,14 +47,14 @@
|
|||||||
"@push.rocks/npmextra": "^5.3.3",
|
"@push.rocks/npmextra": "^5.3.3",
|
||||||
"@push.rocks/projectinfo": "^5.0.1",
|
"@push.rocks/projectinfo": "^5.0.1",
|
||||||
"@push.rocks/qenv": "^6.1.3",
|
"@push.rocks/qenv": "^6.1.3",
|
||||||
"@push.rocks/smartacme": "^5.0.0",
|
"@push.rocks/smartacme": "^8.0.0",
|
||||||
"@push.rocks/smartbucket": "^3.3.10",
|
"@push.rocks/smartbucket": "^3.3.10",
|
||||||
"@push.rocks/smartcli": "^4.0.11",
|
"@push.rocks/smartcli": "^4.0.11",
|
||||||
"@push.rocks/smartclickhouse": "^2.0.17",
|
"@push.rocks/smartclickhouse": "^2.0.17",
|
||||||
"@push.rocks/smartdata": "^5.16.1",
|
"@push.rocks/smartdata": "^5.16.4",
|
||||||
"@push.rocks/smartdelay": "^3.0.5",
|
"@push.rocks/smartdelay": "^3.0.5",
|
||||||
"@push.rocks/smartexit": "^1.0.23",
|
"@push.rocks/smartexit": "^1.0.23",
|
||||||
"@push.rocks/smartexpect": "^1.6.1",
|
"@push.rocks/smartexpect": "^2.5.0",
|
||||||
"@push.rocks/smartfile": "^11.2.7",
|
"@push.rocks/smartfile": "^11.2.7",
|
||||||
"@push.rocks/smartguard": "^3.1.0",
|
"@push.rocks/smartguard": "^3.1.0",
|
||||||
"@push.rocks/smartjson": "^5.0.19",
|
"@push.rocks/smartjson": "^5.0.19",
|
||||||
@@ -62,9 +62,9 @@
|
|||||||
"@push.rocks/smartlog": "^3.1.8",
|
"@push.rocks/smartlog": "^3.1.8",
|
||||||
"@push.rocks/smartlog-destination-clickhouse": "^1.0.13",
|
"@push.rocks/smartlog-destination-clickhouse": "^1.0.13",
|
||||||
"@push.rocks/smartlog-interfaces": "^3.0.2",
|
"@push.rocks/smartlog-interfaces": "^3.0.2",
|
||||||
"@push.rocks/smartpath": "^5.0.18",
|
"@push.rocks/smartpath": "^6.0.0",
|
||||||
"@push.rocks/smartpromise": "^4.2.3",
|
"@push.rocks/smartpromise": "^4.2.3",
|
||||||
"@push.rocks/smartrequest": "^2.1.0",
|
"@push.rocks/smartrequest": "^4.2.2",
|
||||||
"@push.rocks/smartrx": "^3.0.10",
|
"@push.rocks/smartrx": "^3.0.10",
|
||||||
"@push.rocks/smartssh": "^2.0.1",
|
"@push.rocks/smartssh": "^2.0.1",
|
||||||
"@push.rocks/smartstate": "^2.0.26",
|
"@push.rocks/smartstate": "^2.0.26",
|
||||||
|
520
pnpm-lock.yaml
generated
520
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -14,8 +14,10 @@ tap.preTask('should start cloudly', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
tap.preTask('should create a new machine user for testing', async () => {
|
tap.preTask('should create a new machine user for testing', async () => {
|
||||||
|
console.log('🔵 PreTask: Creating first machine user...');
|
||||||
const machineUser = new testCloudly.authManager.CUser();
|
const machineUser = new testCloudly.authManager.CUser();
|
||||||
machineUser.id = await testCloudly.authManager.CUser.getNewId();
|
machineUser.id = await testCloudly.authManager.CUser.getNewId();
|
||||||
|
console.log(` - User ID: ${machineUser.id}`);
|
||||||
machineUser.data = {
|
machineUser.data = {
|
||||||
type: 'machine',
|
type: 'machine',
|
||||||
username: 'test',
|
username: 'test',
|
||||||
@@ -27,48 +29,103 @@ tap.preTask('should create a new machine user for testing', async () => {
|
|||||||
}],
|
}],
|
||||||
role: 'admin',
|
role: 'admin',
|
||||||
};
|
};
|
||||||
|
console.log(` - Username: ${machineUser.data.username}`);
|
||||||
|
console.log(` - Role: ${machineUser.data.role}`);
|
||||||
|
console.log(` - Token: 'test'`);
|
||||||
|
console.log(` - Token roles: ${machineUser.data.tokens[0].assignedRoles}`);
|
||||||
await machineUser.save();
|
await machineUser.save();
|
||||||
|
console.log('✅ PreTask: First machine user saved successfully');
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('should create a new cloudlyApiClient', async () => {
|
tap.test('should create a new cloudlyApiClient', async () => {
|
||||||
|
console.log('🔵 Test: Creating CloudlyApiClient...');
|
||||||
testClient = new cloudlyApiClient.CloudlyApiClient({
|
testClient = new cloudlyApiClient.CloudlyApiClient({
|
||||||
registerAs: 'api',
|
registerAs: 'api',
|
||||||
cloudlyUrl: `http://${helpers.testCloudlyConfig.publicUrl}:${helpers.testCloudlyConfig.publicPort}`,
|
cloudlyUrl: `http://${helpers.testCloudlyConfig.publicUrl}:${helpers.testCloudlyConfig.publicPort}`,
|
||||||
});
|
});
|
||||||
|
console.log(` - URL: http://${helpers.testCloudlyConfig.publicUrl}:${helpers.testCloudlyConfig.publicPort}`);
|
||||||
await testClient.start();
|
await testClient.start();
|
||||||
|
console.log('✅ CloudlyApiClient started successfully');
|
||||||
expect(testClient).toBeTruthy();
|
expect(testClient).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('create a new machine user', async () => {
|
tap.test('DEBUG: Check existing users', async () => {
|
||||||
const machineUser = await testCloudly.authManager.CUser.createMachineUser('test', 'api');
|
console.log('🔍 DEBUG: Checking existing users in database...');
|
||||||
machineUser.data.tokens.push({
|
const allUsers = await testCloudly.authManager.CUser.getInstances({});
|
||||||
token: 'test',
|
console.log(` - Total users found: ${allUsers.length}`);
|
||||||
expiresAt: Date.now() + 3600 * 1000 * 24 * 365,
|
for (const user of allUsers) {
|
||||||
assignedRoles: ['api'],
|
console.log(` - User: ${user.data.username} (ID: ${user.id})`);
|
||||||
})
|
console.log(` - Type: ${user.data.type}`);
|
||||||
await machineUser.save();
|
console.log(` - Role: ${user.data.role}`);
|
||||||
})
|
console.log(` - Tokens: ${user.data.tokens.length}`);
|
||||||
|
for (const token of user.data.tokens) {
|
||||||
|
console.log(` - Token: '${token.token}' | Roles: ${token.assignedRoles?.join(', ')}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
tap.test('should get an identity', async () => {
|
tap.test('should get an identity', async () => {
|
||||||
const identity = await testClient.getIdentityByToken('test');
|
console.log('🔵 Test: Getting identity by token...');
|
||||||
expect(identity).toBeTruthy();
|
console.log(` - Using token: 'test'`);
|
||||||
console.log(identity);
|
console.log(` - API URL: http://${helpers.testCloudlyConfig.publicUrl}:${helpers.testCloudlyConfig.publicPort}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const identity = await testClient.getIdentityByToken('test');
|
||||||
|
console.log('✅ Identity retrieved successfully:');
|
||||||
|
console.log(` - Identity exists: ${!!identity}`);
|
||||||
|
if (identity) {
|
||||||
|
console.log(` - Identity data:`, JSON.stringify(identity, null, 2));
|
||||||
|
}
|
||||||
|
expect(identity).toBeTruthy();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Failed to get identity:');
|
||||||
|
console.error(` - Error message: ${error.message}`);
|
||||||
|
console.error(` - Error stack:`, error.stack);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let image: Image;
|
let image: Image;
|
||||||
tap.test('should create and upload an image', async () => {
|
tap.test('should create and upload an image', async () => {
|
||||||
image = await testClient.image.createImage({
|
console.log('🔵 Test: Creating and uploading image...');
|
||||||
name: 'test',
|
console.log(` - Image name: 'test'`);
|
||||||
description: 'test'
|
console.log(` - Image description: 'test'`);
|
||||||
});
|
|
||||||
console.log('created image: ', image);
|
try {
|
||||||
expect(image).toBeInstanceOf(Image);
|
image = await testClient.image.createImage({
|
||||||
|
name: 'test',
|
||||||
|
description: 'test'
|
||||||
|
});
|
||||||
|
console.log('✅ Image created successfully:');
|
||||||
|
console.log(` - Image ID: ${image?.id}`);
|
||||||
|
console.log(` - Image data:`, image);
|
||||||
|
expect(image).toBeInstanceOf(Image);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Failed to create image:');
|
||||||
|
console.error(` - Error message: ${error.message}`);
|
||||||
|
console.error(` - Error stack:`, error.stack);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
tap.test('should upload an image version', async () => {
|
tap.test('should upload an image version', async () => {
|
||||||
const imageStream = await helpers.getAlpineImageReadableStream();
|
console.log('🔵 Test: Uploading image version...');
|
||||||
|
console.log(` - Version: 'v1.0.0'`);
|
||||||
await image.pushImageVersion('v1.0.0', imageStream);
|
console.log(` - Image exists: ${!!image}`);
|
||||||
|
console.log(` - Image ID: ${image?.id}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const imageStream = await helpers.getAlpineImageReadableStream();
|
||||||
|
console.log(' - Image stream obtained successfully');
|
||||||
|
|
||||||
|
await image.pushImageVersion('v1.0.0', imageStream);
|
||||||
|
console.log('✅ Image version uploaded successfully');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Failed to upload image version:');
|
||||||
|
console.error(` - Error message: ${error.message}`);
|
||||||
|
console.error(` - Error stack:`, error.stack);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('should stop the apiclient', async (toolsArg) => {
|
tap.test('should stop the apiclient', async (toolsArg) => {
|
||||||
|
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@serve.zone/cloudly',
|
name: '@serve.zone/cloudly',
|
||||||
version: '5.0.4',
|
version: '5.0.5',
|
||||||
description: 'A comprehensive tool for managing containerized applications across multiple cloud providers using Docker Swarmkit, featuring web, CLI, and API interfaces.'
|
description: 'A comprehensive tool for managing containerized applications across multiple cloud providers using Docker Swarmkit, featuring web, CLI, and API interfaces.'
|
||||||
}
|
}
|
||||||
|
@@ -16,24 +16,23 @@ export class CloudlyCoreflowManager {
|
|||||||
|
|
||||||
this.typedRouter.addTypedHandler<plugins.servezoneInterfaces.requests.identity.IRequest_Any_Cloudly_CoreflowManager_GetIdentityByToken>(
|
this.typedRouter.addTypedHandler<plugins.servezoneInterfaces.requests.identity.IRequest_Any_Cloudly_CoreflowManager_GetIdentityByToken>(
|
||||||
new plugins.typedrequest.TypedHandler('getIdentityByToken', async (requestData) => {
|
new plugins.typedrequest.TypedHandler('getIdentityByToken', async (requestData) => {
|
||||||
|
// Use getInstance with $elemMatch for querying nested arrays
|
||||||
const user = await this.cloudlyRef.authManager.CUser.getInstance({
|
const user = await this.cloudlyRef.authManager.CUser.getInstance({
|
||||||
data: {
|
data: {
|
||||||
tokens: [
|
tokens: {
|
||||||
{
|
$elemMatch: { token: requestData.token },
|
||||||
token: requestData.token,
|
},
|
||||||
},
|
},
|
||||||
], // find the proper user here.
|
|
||||||
} as any,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new plugins.typedrequest.TypedResponseError(
|
throw new plugins.typedrequest.TypedResponseError(
|
||||||
'The supplied token is not valid. No matching user found.',
|
'The supplied token is not valid. No matching user found.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (user.data.type !== 'machine') {
|
if (user.data.type !== 'machine') {
|
||||||
throw new plugins.typedrequest.TypedResponseError(
|
throw new plugins.typedrequest.TypedResponseError(
|
||||||
'The supplied token is not valid. The user is not a machine.',
|
'The supplied token is not valid. The user is not a machine.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let cluster: Cluster;
|
let cluster: Cluster;
|
||||||
@@ -61,7 +60,7 @@ export class CloudlyCoreflowManager {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// lets enable the getting of cluster configs
|
// lets enable the getting of cluster configs
|
||||||
@@ -76,10 +75,10 @@ export class CloudlyCoreflowManager {
|
|||||||
console.log('got cluster config and sending it back to coreflow');
|
console.log('got cluster config and sending it back to coreflow');
|
||||||
return {
|
return {
|
||||||
configData: await cluster.createSavableObject(),
|
configData: await cluster.createSavableObject(),
|
||||||
deploymentDirectives: [],
|
services: [],
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// lets enable getting of certificates
|
// lets enable getting of certificates
|
||||||
@@ -89,14 +88,14 @@ export class CloudlyCoreflowManager {
|
|||||||
async (dataArg) => {
|
async (dataArg) => {
|
||||||
console.log(`incoming API request for certificate ${dataArg.domainName}`);
|
console.log(`incoming API request for certificate ${dataArg.domainName}`);
|
||||||
const cert = await this.cloudlyRef.letsencryptConnector.getCertificateForDomain(
|
const cert = await this.cloudlyRef.letsencryptConnector.getCertificateForDomain(
|
||||||
dataArg.domainName,
|
dataArg.domainName
|
||||||
);
|
);
|
||||||
console.log(`got certificate ready for reponse ${dataArg.domainName}`);
|
console.log(`got certificate ready for reponse ${dataArg.domainName}`);
|
||||||
return {
|
return {
|
||||||
certificate: await cert.createSavableObject(),
|
certificate: await cert.createSavableObject(),
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@serve.zone/cloudly',
|
name: '@serve.zone/cloudly',
|
||||||
version: '5.0.4',
|
version: '5.0.5',
|
||||||
description: 'A comprehensive tool for managing containerized applications across multiple cloud providers using Docker Swarmkit, featuring web, CLI, and API interfaces.'
|
description: 'A comprehensive tool for managing containerized applications across multiple cloud providers using Docker Swarmkit, featuring web, CLI, and API interfaces.'
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user