mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-04 14:57:42 +00:00
fix: Scope GitHub skill identity lookup by tenant
This commit is contained in:
parent
c7275bd945
commit
356012f699
4 changed files with 59 additions and 1 deletions
|
|
@ -463,6 +463,11 @@ describe('createGitHubSkillSyncRunner', () => {
|
|||
|
||||
expect(result.status).toBe('completed');
|
||||
expect(observedTenantId).toBe('tenant-a');
|
||||
expect(deps.findSkillBySourceIdentity).toHaveBeenCalledWith({
|
||||
source: 'github',
|
||||
upstreamId: 'librechat-skills:skills/research',
|
||||
tenantId: 'tenant-a',
|
||||
});
|
||||
expect(deps.createSkill).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ name: 'research', tenantId: 'tenant-a' }),
|
||||
);
|
||||
|
|
@ -778,6 +783,7 @@ describe('createGitHubSkillSyncRunner', () => {
|
|||
expect(deps.findSkillBySourceIdentity).toHaveBeenCalledWith({
|
||||
source: 'github',
|
||||
upstreamId: 'librechat-skills:skills/research',
|
||||
tenantId: undefined,
|
||||
});
|
||||
expect(deps.createSkill).not.toHaveBeenCalled();
|
||||
expect(deps.updateSkill).toHaveBeenCalledWith(
|
||||
|
|
|
|||
|
|
@ -157,6 +157,7 @@ export type GitHubSkillSyncDeps = {
|
|||
findSkillBySourceIdentity: (params: {
|
||||
source: 'github' | 'notion';
|
||||
upstreamId: string;
|
||||
tenantId?: string;
|
||||
}) => Promise<(ISkill & { _id: Types.ObjectId }) | null>;
|
||||
listSkillsBySource: (params: {
|
||||
source: 'github' | 'notion';
|
||||
|
|
@ -803,8 +804,12 @@ async function prepareRemoteSkill(params: {
|
|||
source: PROVIDER,
|
||||
sourceMetadata,
|
||||
};
|
||||
const foundExisting = await deps.findSkillBySourceIdentity({ source: PROVIDER, upstreamId });
|
||||
const sourceTenantId = source.tenantId ?? undefined;
|
||||
const foundExisting = await deps.findSkillBySourceIdentity({
|
||||
source: PROVIDER,
|
||||
upstreamId,
|
||||
tenantId: sourceTenantId,
|
||||
});
|
||||
const existing =
|
||||
foundExisting && (foundExisting.tenantId ?? undefined) === sourceTenantId
|
||||
? foundExisting
|
||||
|
|
|
|||
|
|
@ -507,6 +507,48 @@ describe('Skill CRUD methods', () => {
|
|||
expect(await SkillFile.countDocuments({ skillId: skill._id })).toBe(0);
|
||||
});
|
||||
|
||||
it('findSkillBySourceIdentity searches only the requested tenant bucket', async () => {
|
||||
const upstreamId = 'librechat-skills:skills/research';
|
||||
const sourceMetadata = {
|
||||
provider: 'github',
|
||||
sourceId: 'librechat-skills',
|
||||
upstreamId,
|
||||
};
|
||||
const author = new mongoose.Types.ObjectId();
|
||||
const tenantSkill = await Skill.create({
|
||||
name: 'research',
|
||||
description: 'A tenant-scoped GitHub skill mirror.',
|
||||
body: 'tenant body',
|
||||
author,
|
||||
authorName: 'GitHub Sync',
|
||||
tenantId: 'tenant-a',
|
||||
source: 'github',
|
||||
sourceMetadata,
|
||||
});
|
||||
const ambientSkill = await Skill.create({
|
||||
name: 'research',
|
||||
description: 'An ambient GitHub skill mirror.',
|
||||
body: 'ambient body',
|
||||
author,
|
||||
authorName: 'GitHub Sync',
|
||||
source: 'github',
|
||||
sourceMetadata,
|
||||
});
|
||||
|
||||
const ambientResult = await methods.findSkillBySourceIdentity({
|
||||
source: 'github',
|
||||
upstreamId,
|
||||
});
|
||||
const tenantResult = await methods.findSkillBySourceIdentity({
|
||||
source: 'github',
|
||||
upstreamId,
|
||||
tenantId: 'tenant-a',
|
||||
});
|
||||
|
||||
expect(ambientResult?._id.toString()).toBe(ambientSkill._id.toString());
|
||||
expect(tenantResult?._id.toString()).toBe(tenantSkill._id.toString());
|
||||
});
|
||||
|
||||
it('listSkillsByAccess returns only accessible skills and paginates by cursor', async () => {
|
||||
const ids: mongoose.Types.ObjectId[] = [];
|
||||
for (let i = 0; i < 3; i++) {
|
||||
|
|
|
|||
|
|
@ -1365,11 +1365,16 @@ export function createSkillMethods(mongoose: typeof import('mongoose'), deps: Sk
|
|||
async function findSkillBySourceIdentity(params: {
|
||||
source: 'github' | 'notion';
|
||||
upstreamId: string;
|
||||
tenantId?: string;
|
||||
}): Promise<(ISkill & { _id: Types.ObjectId }) | null> {
|
||||
const Skill = mongoose.models.Skill as Model<ISkillDocument>;
|
||||
const tenantFilter: FilterQuery<ISkillDocument> = params.tenantId
|
||||
? { tenantId: params.tenantId }
|
||||
: { $or: [{ tenantId: { $exists: false } }, { tenantId: null }] };
|
||||
const doc = await Skill.findOne({
|
||||
source: params.source,
|
||||
'sourceMetadata.upstreamId': params.upstreamId,
|
||||
...tenantFilter,
|
||||
}).lean();
|
||||
return (doc as unknown as (ISkill & { _id: Types.ObjectId }) | null) ?? null;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue