Skip to content

Commit

Permalink
feat(user-api): implement unset password methods for related user api…
Browse files Browse the repository at this point in the history
… resources #63
  • Loading branch information
chfoidl committed Jan 18, 2024
1 parent dd167da commit 6d2173f
Show file tree
Hide file tree
Showing 3 changed files with 283 additions and 0 deletions.
79 changes: 79 additions & 0 deletions packages/user-api/src/DrupalkitUserApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ export const DrupalkitUserApi = (
const setPasswordEndpoint =
drupalkitOptions.userApiSetPasswordEndpoint ?? "/user-api/set-password";

const initUnetPasswordEndpoint =
drupalkitOptions.userApiInitUnsetPasswordEndpoint ??
"/user-api/unset-password/init";
const unsetPasswordEndpoint =
drupalkitOptions.userApiUnsetPasswordEndpoint ?? "/user-api/unset-password";

const passwordlessLoginEndpoint =
drupalkitOptions.userApiPasswordlessLoginEndpoint ??
"/user-api/passwordless-login";
Expand Down Expand Up @@ -281,6 +287,77 @@ export const DrupalkitUserApi = (
return Result.Ok(result.val.data);
};

/**
* Initialize unset password.
*
* The request MUST be authorized!
*
* This endpoint does not return useful data.
* Only the status code is important.
*
* @param email - E-Mail address of the user.
* @param requestOptions - Optional request options.
*/
const initUnsetPassword = async (
email: string,
requestOptions?: OverrideableRequestOptions,
): Promise<Result<SuccessResponse, DrupalkitError>> => {
const url = drupalkit.buildUrl(initUnetPasswordEndpoint);

const result = await drupalkit.request<SuccessResponse>(
url,
{
method: "POST",
body: { email },
headers,
},
requestOptions,
);

if (result.err) {
return result;
}

return Result.Ok(result.val.data);
};

/**
* Unset user password.
*
* This endpoint must either supply the current password or
* be verified via the verification plugin!
*
* @param currentPassword - The current password for the user.
* @param requestOptions - Optional request options.
*/
const unsetPassword = async (
currentPassword?: string,
requestOptions?: OverrideableRequestOptions,
): Promise<Result<SuccessResponse, DrupalkitError>> => {
const url = drupalkit.buildUrl(unsetPasswordEndpoint);

const payload: { currentPassword?: string } = {};
if (currentPassword) {
payload.currentPassword = currentPassword;
}

const result = await drupalkit.request<SuccessResponse>(
url,
{
method: "POST",
body: payload,
headers,
},
requestOptions,
);

if (result.err) {
return result;
}

return Result.Ok(result.val.data);
};

/**
* Trigger passwordless login.
*
Expand Down Expand Up @@ -393,6 +470,8 @@ export const DrupalkitUserApi = (
cancelAccount,
initSetPassword,
setPassword,
initUnsetPassword,
unsetPassword,
passwordlessLogin,
initSetEmail,
setEmail,
Expand Down
12 changes: 12 additions & 0 deletions packages/user-api/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@ declare module "@drupal-kit/core" {
* @default '/user-api/set-password'
*/
userApiSetPasswordEndpoint?: string;
/**
* Endpoint path for the User API InitSetPasswordResource.
*
* @default '/user-api/set-password/init'
*/
userApiInitUnsetPasswordEndpoint?: string;
/**
* Endpoint path for the User API SetPasswordResource.
*
* @default '/user-api/set-password'
*/
userApiUnsetPasswordEndpoint?: string;
/**
* Endpoint path for the User API PasswordlessLoginResource.
*
Expand Down
192 changes: 192 additions & 0 deletions packages/user-api/tests/DrupalkitUserApi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,198 @@ test.serial("Handle error while set password", async (t) => {
t.assert(result.err);
});

/**
* Init unset password
*/

test.serial("Init unset password", async (t) => {
t.plan(3);

const drupalkit = createDrupalkit();
const email = "JzWZg@example.com";

server.use(
rest.post("*/user-api/unset-password/init", async (req, res, ctx) => {
t.is(req.headers.get("content-type"), "application/json");

t.deepEqual(await req.json(), { email });

return res(ctx.json(successResponse));
}),
);

const result = await drupalkit.userApi.initUnsetPassword(email);

const res = result.unwrap();

t.deepEqual(res, successResponse);
});

test.serial("Init unset password with custom request options", async (t) => {
t.plan(2);

const drupalkit = createDrupalkit();
const email = "JzWZg@example.com";

drupalkit.hook.before("request", (options) => {
t.is(options.cache, "no-cache");
});

server.use(
rest.post("*/user-api/unset-password/init", async (req, res, ctx) => {
t.is(req.headers.get("X-Custom"), "1");

return res(ctx.json(successResponse));
}),
);

await drupalkit.userApi.initUnsetPassword(email, {
cache: "no-cache",
headers: {
"X-Custom": "1",
},
});
});

test.serial("Init unset password with custom endpoint", async (t) => {
const drupalkit = createDrupalkit({
baseUrl: BASE_URL,
userApiInitUnsetPasswordEndpoint: "/custom/unset-password/init",
});
const email = "JzWZg@example.com";

server.use(
rest.post("*/custom/unset-password/init", async (_req, res, ctx) =>
res(ctx.json(successResponse)),
),
);

const result = await drupalkit.userApi.initUnsetPassword(email);

t.assert(result.ok);
});

test.serial("Handle error while init unset password", async (t) => {
const drupalkit = createDrupalkit();
const email = "JzWZg@example.com";

server.use(
rest.post("*/user-api/unset-password/init", async (_req, res, ctx) =>
res(ctx.status(400)),
),
);

const result = await drupalkit.userApi.initUnsetPassword(email);

t.assert(result.err);
});

/**
* Unset password
*/

test.serial("Unset password with verification", async (t) => {
t.plan(3);

const drupalkit = createDrupalkit();

server.use(
rest.post("*/user-api/unset-password", async (req, res, ctx) => {
t.is(req.headers.get("content-type"), "application/json");

t.deepEqual(await req.json(), {});

return res(ctx.json(successResponse));
}),
);

const result = await drupalkit.userApi.unsetPassword();

const res = result.unwrap();

t.deepEqual(res, successResponse);
});

test.serial("Unset password with currentPassword", async (t) => {
t.plan(3);

const drupalkit = createDrupalkit();
const currentPassword = "abc123";

server.use(
rest.post("*/user-api/unset-password", async (req, res, ctx) => {
t.is(req.headers.get("content-type"), "application/json");

t.deepEqual(await req.json(), { currentPassword });

return res(ctx.json(successResponse));
}),
);

const result = await drupalkit.userApi.unsetPassword(currentPassword);

const res = result.unwrap();

t.deepEqual(res, successResponse);
});

test.serial("Unset password with custom request options", async (t) => {
t.plan(2);

const drupalkit = createDrupalkit();

drupalkit.hook.before("request", (options) => {
t.is(options.cache, "no-cache");
});

server.use(
rest.post("*/user-api/unset-password", async (req, res, ctx) => {
t.is(req.headers.get("X-Custom"), "1");

return res(ctx.json(successResponse));
}),
);

await drupalkit.userApi.unsetPassword(undefined, {
cache: "no-cache",
headers: {
"X-Custom": "1",
},
});
});

test.serial("Unset password with custom endpoint", async (t) => {
const drupalkit = createDrupalkit({
baseUrl: BASE_URL,
userApiUnsetPasswordEndpoint: "/custom/unset-password",
});

server.use(
rest.post("*/custom/unset-password", async (_req, res, ctx) =>
res(ctx.json(successResponse)),
),
);

const result = await drupalkit.userApi.unsetPassword();

t.assert(result.ok);
});

test.serial("Handle error while unset password", async (t) => {
const drupalkit = createDrupalkit();
const newPassword = "new-password";

server.use(
rest.post("*/user-api/unset-password", async (_req, res, ctx) =>
res(ctx.status(400)),
),
);

const result = await drupalkit.userApi.unsetPassword(newPassword);

t.assert(result.err);
});

/**
* Passwordless login
*/
Expand Down

0 comments on commit 6d2173f

Please sign in to comment.