Skip to content

Commit e56a577

Browse files
committed
Merge branch 'refactor/actor-resolution' into feat/delete-actor
2 parents 4526d1c + 339295c commit e56a577

2 files changed

Lines changed: 26 additions & 7 deletions

File tree

src/tools/actors/actor_helpers.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,20 @@ export type TargetActor = {
5656
actor: Actor | undefined;
5757
};
5858

59+
/** Throws `UserInputError` when the Actor the API returned lives in another account than the caller's. */
60+
function validateActorOwner(actor: Actor, username: string, bareName: string): void {
61+
if (actor.username.toLowerCase() === username.toLowerCase()) return;
62+
throw new UserInputError(
63+
`This tool works only with Actors of your own account (${username}); Actor ${bareName} belongs to ${actor.username}.`,
64+
);
65+
}
66+
5967
/**
6068
* Looks up the caller's username and the Actor. A `username/` or `username~` prefix must name the caller's
6169
* own account. A bare value that no Actor is named after and that has the shape of an Actor ID, the value
6270
* the build and run tools hand out, is looked up once as an ID and must be the caller's Actor. `actor` is
63-
* undefined when nothing matches: `push-actor` then creates an Actor of that name, the other tools that
64-
* write an Actor report it as not found; an ID never creates one.
71+
* undefined when nothing matches: `push-actor` then creates an Actor of that name, the other tools report
72+
* it as not found; an ID never creates one.
6573
*/
6674
export async function resolveTargetActor(
6775
client: ApifyClient,
@@ -75,14 +83,12 @@ export async function resolveTargetActor(
7583
}
7684
const actorClient = client.actor(formatActorFullName(username, bareName));
7785
const actor = await actorClient.get();
86+
// The platform resolves the old `username~name` of an Actor moved to another account to that Actor.
87+
if (actor) validateActorOwner(actor, username, bareName);
7888
const canBeId = actor === undefined && ownerPrefix === undefined && ACTOR_ID_SHAPE_REGEX.test(bareName);
7989
if (!canBeId) return { actorClient, username, bareName, actor };
8090
const actorById = await client.actor(bareName).get();
8191
if (!actorById) return { actorClient, username, bareName, actor: undefined };
82-
if (actorById.username.toLowerCase() !== username.toLowerCase()) {
83-
throw new UserInputError(
84-
`This tool works only with Actors of your own account (${username}); Actor ${bareName} belongs to ${actorById.username}.`,
85-
);
86-
}
92+
validateActorOwner(actorById, username, bareName);
8793
return { actorClient: client.actor(actorById.id), username, bareName: actorById.name, actor: actorById };
8894
}

tests/unit/tools.push_actor.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,18 @@ describe('push-actor', () => {
420420
expect(actorGetMock).not.toHaveBeenCalled();
421421
expectNoWrite();
422422
});
423+
424+
it('refuses a name the platform resolves to an Actor moved to another account before any write', async () => {
425+
actorGetMock.mockResolvedValue({ ...mockActor(), username: 'jane' });
426+
427+
const { text } = await callToolExpectingUserError({ actor: 'my-actor', files: [MAIN_JS] });
428+
429+
expect(text).toBe(
430+
'This tool works only with Actors of your own account (john); Actor my-actor belongs to jane.',
431+
);
432+
expect(actorMock).toHaveBeenCalledTimes(1);
433+
expectNoWrite();
434+
});
423435
});
424436

425437
describe('source archive', () => {
@@ -1161,6 +1173,7 @@ describe('push-actor', () => {
11611173

11621174
it('accepts a username prefix with a dot in the tilde form', async () => {
11631175
userGetMock.mockResolvedValue({ username: 'john.doe', id: 'user-secret' });
1176+
actorGetMock.mockResolvedValue({ ...mockActor(), username: 'john.doe' });
11641177

11651178
const { structuredContent } = await callTool({
11661179
actor: 'john.doe~my-actor',

0 commit comments

Comments
 (0)