Skip to content

Commit

Permalink
Merge pull request #267 from mnfst/265-updates-clear-all-the-item-ent…
Browse files Browse the repository at this point in the history
…ity-relationship-fields

✨ PATCH items (CRUD)
  • Loading branch information
brunobuddy authored Jan 23, 2025
2 parents c475dcc + b250eeb commit 0eecb95
Show file tree
Hide file tree
Showing 25 changed files with 762 additions and 94 deletions.
6 changes: 6 additions & 0 deletions .changeset/stale-carrots-push.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'manifest': minor
'@mnfst/sdk': minor
---

Added PATCH requests for item update
22 changes: 22 additions & 0 deletions .github/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name-template: 'v$NEXT_PATCH_VERSION'
tag-template: 'v$NEXT_PATCH_VERSION'
categories:
- title: '🚀 Features'
labels:
- enhancement
- title: '🐛 Bug Fixes'
labels:
- bug
- title: '🛠 Maintenance'
labels:
- chore
- refactor
- tests
- dependencies
change-template: '- $TITLE (#$NUMBER)'
no-changes-template: '- No changes'

template: |
## What's Changed
$CHANGES
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Release
name: Publish

on:
push:
Expand Down
30 changes: 30 additions & 0 deletions .github/workflows/release-drafter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Release Drafter

on:
push:
tags:
- 'manifest@*.*.*' # Match tags for "manifest"
- '@mnfst/sdk@*.*.*' # Match tags for "@mnfst/sdk"
- 'add-manifest@*.*.*' # Match tags for "add-manifest"
workflow_dispatch: # Allow manual triggering of the workflow

permissions:
contents: write
pull-requests: write

jobs:
release:
runs-on: ubuntu-latest

steps:
# Step 1: Checkout the repository
- name: Checkout repository
uses: actions/checkout@v3

# Step 2: Run Release Drafter to draft and publish the release
- name: Draft and Publish Release
uses: release-drafter/release-drafter@v5
with:
config-name: release.yml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
9 changes: 8 additions & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,12 @@ export default [
},
{ languageOptions: { globals: { ...globals.browser, ...globals.node } } },
pluginJs.configs.recommended,
...tseslint.configs.recommended
...tseslint.configs.recommended,
{
// Allow "any" in test files.
files: ['**/*.spec.ts', '**/*.e2e-spec.ts'],
rules: {
'@typescript-eslint/no-explicit-any': 'off'
}
}
]
141 changes: 106 additions & 35 deletions packages/core/manifest/e2e/tests/collection-crud.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ describe('Collection CRUD (e2e)', () => {
location: { lat: 12, lng: 13 }
}

it('POST /collections/:entity', async () => {
const response = await global.request
.post('/collections/dogs')
.send(dummyDog)
describe('POST /collections/:entity', () => {
it('should create an item', async () => {
const response = await global.request
.post('/collections/dogs')
.send(dummyDog)

expect(response.status).toBe(201)
expect(response.status).toBe(201)
})
})

describe('GET /collections/:entity', () => {
Expand Down Expand Up @@ -69,52 +71,121 @@ describe('Collection CRUD (e2e)', () => {
})
})

it('GET /collections/:entity/select-options', async () => {
const response = await global.request.get(
'/collections/dogs/select-options'
)
describe('GET /collections/:entity/select-options', () => {
it('should get select options', async () => {
const response = await global.request.get(
'/collections/dogs/select-options'
)

expect(response.status).toBe(200)
expect(response.body).toMatchObject<SelectOption[]>([
{
label: dummyDog.name,
id: 1
}
])
expect(response.status).toBe(200)
expect(response.body).toMatchObject<SelectOption[]>([
{
label: dummyDog.name,
id: 1
}
])
})
})

it('GET /collections/:entity/:id', async () => {
const response = await global.request.get('/collections/dogs/1')
describe('GET /collections/:entity/:id', () => {
it('should return an item', async () => {
const response = await global.request.get('/collections/dogs/1')

expect(response.status).toBe(200)
expect(response.body).toMatchObject(dummyDog)
expect(response.status).toBe(200)
expect(response.body).toMatchObject(dummyDog)
})
})

describe('PUT /collections/:entity/:id', () => {
it('should fully update an item', async () => {
const newName = 'Rex'

const response = await global.request.put('/collections/dogs/1').send({
name: newName
})

expect(response.status).toBe(200)

const updatedResponse = await global.request.get('/collections/dogs/1')

expect(updatedResponse.status).toBe(200)
expect(updatedResponse.body).toMatchObject({
name: newName
})
})
})

it('PUT /collections/:entity/:id', async () => {
const newName = 'Rex'
describe('PATCH /collections/:entity/:id', () => {
it('should patch an item', async () => {
const postResponse = await global.request
.post('/collections/dogs')
.send(dummyDog)

const newAge = 6

const response = await global.request
.patch(`/collections/dogs/${postResponse.body.id}`)
.send({
age: newAge
})

expect(response.status).toBe(200)

const response = await global.request.put('/collections/dogs/1').send({
name: newName
const updatedResponse = await global.request.get(
`/collections/dogs/${postResponse.body.id}`
)

expect(updatedResponse.status).toBe(200)
expect(updatedResponse.body).toMatchObject({
...dummyDog,
age: newAge
})
})

expect(response.status).toBe(200)
it('should keep relations if not provided', async () => {
const createOwnerResponse = await global.request
.post('/collections/owners')
.send({
name: 'John Doe'
})

const updatedResponse = await global.request.get('/collections/dogs/1')
const dogWithOwner = {
name: 'Charlie',
ownerId: createOwnerResponse.body.id
}

expect(updatedResponse.status).toBe(200)
expect(updatedResponse.body).toMatchObject({
...dummyDog,
name: newName
const createResponse = await global.request
.post('/collections/dogs')
.send(dogWithOwner)

expect(createResponse.status).toBe(201)

const updateResponse = await global.request
.patch(`/collections/dogs/${createResponse.body.id}`)
.send({
name: 'Charlie 2'
})

expect(updateResponse.status).toBe(200)

const fetchResponse = await global.request.get(
`/collections/dogs/${createResponse.body.id}?relations=owner`
)

expect(fetchResponse.status).toBe(200)
expect(fetchResponse.body?.owner?.id).toEqual(1)
})
})

it('DELETE /collections/:entity/:id', async () => {
const response = await global.request.delete('/collections/dogs/1')
describe('DELETE /collections/:entity/:id', () => {
it('should delete an item', async () => {
const response = await global.request.delete('/collections/dogs/1')

expect(response.status).toBe(200)
expect(response.status).toBe(200)

const updatedResponse = await global.request.get('/collections/dogs/1')
const updatedResponse = await global.request.get('/collections/dogs/1')

expect(updatedResponse.status).toBe(404)
expect(updatedResponse.status).toBe(404)
})
})
})
2 changes: 1 addition & 1 deletion packages/core/manifest/e2e/tests/single-crud.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ describe('Single CRUD (e2e)', () => {
})
})

describe('PUT /collections/:entity', () => {
describe('PUT /singles/:entity', () => {
it('can update a single entity', async () => {
const newTitle: string = 'Contact Us'

Expand Down
2 changes: 1 addition & 1 deletion packages/core/manifest/e2e/tests/validation.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ describe('Validation (e2e)', () => {

const updateResponse = await global.request
.put('/collections/super-users/1')
.send({ name: 'new name' })
.send({ name: 'new name', email: 'example2@manifest.build' })

expect(badCreateResponse.status).toBe(400)
expect(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Get,
Param,
ParseIntPipe,
Patch,
Post,
Put,
Query,
Expand Down Expand Up @@ -86,12 +87,27 @@ export class CollectionController {

@Put(':entity/:id')
@Rule('update')
update(
@Param('entity') entity: string,
put(
@Param('entity') entitySlug: string,
@Param('id', ParseIntPipe) id: number,
@Body() entityDto: Partial<BaseEntity>
@Body() itemDto: Partial<BaseEntity>
): Promise<BaseEntity> {
return this.crudService.update({ entitySlug, id, itemDto })
}

@Patch(':entity/:id')
@Rule('update')
patch(
@Param('entity') entitySlug: string,
@Param('id', ParseIntPipe) id: number,
@Body() itemDto: Partial<BaseEntity>
): Promise<BaseEntity> {
return this.crudService.update(entity, id, entityDto)
return this.crudService.update({
entitySlug,
id,
itemDto,
partialReplacement: true
})
}

@Delete(':entity/:id')
Expand Down
23 changes: 19 additions & 4 deletions packages/core/manifest/src/crud/controllers/single.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
Get,
NotFoundException,
Param,
Patch,
Put,
Req,
UseGuards
Expand Down Expand Up @@ -55,10 +56,24 @@ export class SingleController {

@Put(':entity')
@Rule('update')
update(
@Param('entity') entity: string,
@Body() entityDto: Partial<BaseEntity>
put(
@Param('entity') entitySlug: string,
@Body() itemDto: Partial<BaseEntity>
): Promise<BaseEntity> {
return this.crudService.update({ entitySlug, id: 1, itemDto })
}

@Patch(':entity')
@Rule('update')
patch(
@Param('entity') entitySlug: string,
@Body() itemDto: Partial<BaseEntity>
): Promise<BaseEntity> {
return this.crudService.update(entity, 1, entityDto)
return this.crudService.update({
entitySlug,
id: 1,
itemDto,
partialReplacement: true
})
}
}
Loading

0 comments on commit 0eecb95

Please sign in to comment.