Repository: logto-io/logto
File: packages/core/src/routes/admin-user/role.ts (TypeScript)

Code before:

          // Return totalCount to pagination middleware
          ctx.pagination.totalCount = count;
          ctx.body = roles;

          return next();
        },
        (error) => {
          if (error instanceof TypeError) {
            throw new RequestError(
              { code: 'request.invalid_input', details: error.message },
              error
            );
          }
          throw error;
        }
      );
    }
  );

  router.post(
    '/users/:userId/roles',
    koaGuard({
      params: object({ userId: string() }),
      body: object({ roleIds: string().min(1).array() }),
      response: object({ addedRoleIds: string().min(1).array() }),
      status: [201, 404, 422],
    }),
    async (ctx, next) => {
      const {
        params: { userId },
        body: { roleIds },
      } = ctx.guard;

      await findUserById(userId);
      const usersRoles = await findUsersRolesByUserId(userId);
      const existingRoleIds = new Set(usersRoles.map(({ roleId }) => roleId));
      const roleIdsToAdd = roleIds.filter((id) => !existingRoleIds.has(id)); // ignore existing roles.
      const roles = await findRolesByRoleIds(roleIdsToAdd);

      for (const role of roles) {
        assertThat(
          role.type === RoleType.User,
          new RequestError({ code: 'user.invalid_role_type', status: 422, roleId: role.id })
        );
      }

      if (roleIdsToAdd.length > 0) {
        await Promise.all(roleIdsToAdd.map(async (roleId) => findRoleById(roleId)));
        await insertUsersRoles(
          roleIdsToAdd.map((roleId) => ({ id: generateStandardId(), userId, roleId }))

Review comment (suggestion):
```suggestion
      response: object({ roleIds: string().min(1).array() }),
```

Code after:

          // Return totalCount to pagination middleware
          ctx.pagination.totalCount = count;
          ctx.body = roles;

          return next();
        },
        (error) => {
          if (error instanceof TypeError) {
            throw new RequestError(
              { code: 'request.invalid_input', details: error.message },
              error
            );
          }
          throw error;
        }
      );
    }
  );

  router.post(
    '/users/:userId/roles',
    koaGuard({
      params: object({ userId: string() }),
      body: object({ roleIds: string().min(1).array() }),
      response: object({
        roleIds: string().min(1).array(),
        addedRoleIds: string().min(1).array(),
      }),
      status: [201, 404, 422],
    }),
    async (ctx, next) => {
      const {
        params: { userId },
        body: { roleIds },
      } = ctx.guard;

      await findUserById(userId);
      const usersRoles = await findUsersRolesByUserId(userId);
      const existingRoleIds = new Set(usersRoles.map(({ roleId }) => roleId));
      const roleIdsToAdd = roleIds.filter((id) => !existingRoleIds.has(id)); // ignore existing roles.
      const roles = await findRolesByRoleIds(roleIdsToAdd);

      for (const role of roles) {
        assertThat(
          role.type === RoleType.User,
          new RequestError({ code: 'user.invalid_role_type', status: 422, roleId: role.id })
        );
      }

      if (roleIdsToAdd.length > 0) {