-
-
Notifications
You must be signed in to change notification settings - Fork 27
feat: Automatic publication of admins #40
feat: Automatic publication of admins #40
Conversation
Please do review the PR template in the description and fill it out accordingly. |
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The basic functionality (when an admin posts a resource, publish it immediately) works OK. But looking at how that's done, it's clear the suggested flow (.github/CONTRIBUTING.md
) wasn't followed. The checklist in the PR template is intended as a reminder to actually do those things - link the issue, write the tests, update the docs, ...
Functionality
✅ When I suggest a resource as a non-admin, it is created as a draft. When I do it as an admin, it's immediately published.
💡 UI is misleading when an admin is using the Suggest form - it still tells them their submission will be reviewed by an admin:
Implementation
❓ Are we doing too much in the controller - maybe the service should be deciding whether to create a draft or published resource (i.e. receiving both information about the resource and the person creating it, something like service.create(resource, user)
) as that's part of our business logic not specific to to the transport layer.
❌ No E2E test
❌ No API integration test
❓ Do the docs need updating to reflect the changed behaviour of the endpoint?
Key
- ✅ Nothing to change, just commenting
- 💡 Possible change, or suggestion to think about in the future
- ❓ Discussion required before approval
- ❌ Change required before approval
let isDraft = true; | ||
|
||
if (req.superuser || req.user?.is_admin) { | ||
isDraft = false; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Using the optional chaining here is unnecessary - if req.user
was null
or undefined
, this would already have crashed on line 48 (you could also have accessed is_admin
there, to keep all references to the req.user
in one place).
💡 I'd always try to avoid using let
for something like this, we can determine the value straight away and make it const
:
let isDraft = true; | |
if (req.superuser || req.user?.is_admin) { | |
isDraft = false; | |
} | |
const isDraft = (req.superuser || req.user?.is_admin) ? false : true; |
But then having a conditional expression whose values are true
and false
seems a bit weird, just use the value directly (ref: https://en.wikipedia.org/wiki/De_Morgan%27s_laws):
let isDraft = true; | |
if (req.superuser || req.user?.is_admin) { | |
isDraft = false; | |
} | |
const isDraft = !req.superuser && !req.user?.is_admin; |
Thank you for your time and effort in reviewing my code. I have refactored the code in the API section to make it clearer, placing each piece of code in its appropriate file. Additionally, I have modified the responses that appear on the client side. However, as a backend developer, I am facing problems with client-side testing. Could you help me with changing the authentication for the user? The test is failing because it signs in using the admin account by default, but I want to control the sign-in process so I can create tests for both types of roles in the system. Thanks. |
What test is failing, and what exactly is it saying? You can push work in progress to the branch, then it'll run in the pipeline and we can all see it. Across the various existing E2E cases there are examples of signing in using both admin and non-admin users, so testing both roles should be resonably straightforward. |
I apologize for the delayed response. This week, I am celebrating Eid, an important holiday in Iraq , |
|
I have seen those messages. Do you have a specific question? You can see the tests failing in the pipeline, if that's what you're asking about - they give a lot of feedback, and I don't think "failing because it signs in using the admin account by default" is a correct interpretation of what's happening - the client-side tests don't sign in at all, the network layer is mocked with MSW. |
I think every thing related to tested is done now The swagger does not requeired to update I think because the only think that changes is the response of resource creation Also is there any project linked with this repo to link the ticket? Thank you for your time |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the iteration, the test coverage is much improved. How did you find writing them? What about the refactoring from the controller to the service, what did you think of that?
is there any project linked with this repo to link the ticket?
You can just link the issue, #11
if (requestBody.draft) { | ||
await expect( | ||
screen.findByText(/thank you for suggesting a resource/i) | ||
).resolves.toHaveClass("message", "success"); | ||
} else { | ||
await expect( | ||
screen.findByText(/thank you for publishing a resource/i) | ||
).resolves.toHaveClass("message", "success"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❌ You should pretty much never have conditional logic in tests. Only one of these cases is ever actually tested, there's no test where "suggesting" would be shown (which is what made them start failing in the first place), so including the other branch just confuses the issue and implies that's tested when it isn't.
❌ It's also incorrect - whether it shows "suggesting" or "publishing" isn't a matter of the request body (which never has a draft property - this should be clear from the assertions on it), it's a matter of the response body (which is an empty object in all of these tests).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok I got that thanks
<p> | ||
Please use the form below to submit a suggestion. Note that it will not | ||
{isAdmin | ||
? "Please use the form below to submit a resource. It's will appear immediately to the home page because you are administrator" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Typos in UI text
? "Please use the form below to submit a resource. It's will appear immediately to the home page because you are administrator" | |
? "Please use the form below to submit a resource. It will appear immediately on the home page because you are administrator." |
cy.request({ | ||
headers: { Authorization: `Bearer ${Cypress.env("SUDO_TOKEN")}` }, | ||
method: "PATCH", | ||
url: `/api/resources/${created.id}`, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❌ The PATCH
request is to publish the resource, so that it shows up on the home page, which doesn't make sense in a test where: 1. the resource is already published (because the user is an admin); and 2. the home page isn't subsequently visited anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ohhh that right :( mybe I forgot to delete it
@@ -385,4 +385,33 @@ describe("/api/resources", () => { | |||
expect(published).toMatchObject({ publisher: admin.id, source: user.id }); | |||
}); | |||
}); | |||
|
|||
describe("POST / - Auto publish for the admin resource", () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 This should really appear under the existing POST /
group of tests; you can nest describe
blocks where you really need separate contexts:
describe("/api/resources", () => {
describe("POST /", () => {
describe("as a user", () => {
// ...
});
describe("as an admin", () => {
// ...
});
});
});
cy.visit("/"); | ||
|
||
cy.task("seed", { users: [admin] }); | ||
cy.logInAs("[email protected]"); | ||
cy.visit("/"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 It seems odd to visit the page, then update the DB, then login, then reload the home page (which you're on after logging in), maybe:
cy.visit("/"); | |
cy.task("seed", { users: [admin] }); | |
cy.logInAs("[email protected]"); | |
cy.visit("/"); | |
cy.task("seed", { users: [admin] }); | |
cy.visit("/"); | |
cy.logInAs("[email protected]"); |
@@ -12,7 +13,8 @@ export default function Suggest() { | |||
const [topics, setTopics] = useState(undefined); | |||
const resourceService = useService(ResourceService); | |||
const topicService = useService(TopicService); | |||
|
|||
const user = usePrincipal(); | |||
const isAdmin = user && user.is_admin; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 or maybe
const isAdmin = user && user.is_admin; | |
const isAdmin = user?.is_admin; |
text: resource.draft | ||
? "Thank you for suggesting a resource!" | ||
: "Thank you for publishing a resource!", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅ This is neat, basing it on the response means it continues to work even if the logic around roles changes later on.
@@ -41,11 +45,13 @@ export default function Suggest() { | |||
|
|||
return ( | |||
<> | |||
<h2>Suggest a resource</h2> | |||
<h2>{isAdmin ? "Publish a resource" : "Suggest a resource"}</h2> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 or
<h2>{isAdmin ? "Publish a resource" : "Suggest a resource"}</h2> | |
<h2>{isAdmin ? "Publish" : "Suggest"} a resource</h2> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Continuing from the comment on the nav, this now has lots of separate checks of the same thing, which is a bit awkward. It'd be neater to have one higher-level e.g. isAdmin ? <Publish {...} /> : <Suggest {...} />
.
expect(body).toMatchObject({ | ||
title: resource.title, | ||
url: resource.url, | ||
draft: false, | ||
id: expect.stringMatching(patterns.UUID), | ||
source: id, | ||
description: null, | ||
accession: expect.stringMatching(patterns.DATETIME), | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Most of this is already asserted on in other tests, I wonder if the core logic being tested here would be clearer if the assertion was more focused:
expect(body).toMatchObject({ | |
title: resource.title, | |
url: resource.url, | |
draft: false, | |
id: expect.stringMatching(patterns.UUID), | |
source: id, | |
description: null, | |
accession: expect.stringMatching(patterns.DATETIME), | |
}); | |
expect(body).toHaveProperty("draft", false); |
This is now complete from the application perspective. |
thank you for your effors and comments that I will sure work on them in the future |
I have reviewed the client testing and end-to-end (e2e) testing, and I found them to be very useful for completing the task. Regarding the controller logic, I realized I had misunderstood the project's code structure. I initially thought all logic should be placed in the service layer, but I noticed some validation and checks being performed in the controller as well. However, I now understand that the correct approach is indeed to place most of the business logic in the service layer. |
This is a:
Description
login as admin and then add the suggestion and check it in the publication directly
Links
Author checklist
main
from a branch named<category>/<name>
, e.g.feature/edit-spaceships
orbugfix/restore-oxygen