Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix update only stream name also set default to timezone and country code #587

Merged
merged 5 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
### Common
* **core**: `GET /detections` return `Total-items` in response headers

### Bug Fixes
* **core**: Fix update only stream name also set default to timezone and country code

## 1.3.8 (2024-03-xx)
### Common
* **core**: Use Google Map API for getting timezone and country from latitude and longitude
Expand Down
12 changes: 8 additions & 4 deletions common/converter/conversion.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,25 @@ module.exports = class Conversion {
this.target = target
this.property = property

this.value = null
this.value = undefined
this.conversions = []
this.required = true
this.defaultValue = null
this.defaultValue = undefined
}

execute () {
if (this.required && this.src[this.property] == null) {
if (this.required && this.src[this.property] === undefined) {
this.throwError('the parameter is required but was not provided')
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

```
if (this.required && this.src[this.property] == null) {
  this.throwError('the parameter is required but was not provided')
}
```

we should probablyu also throw error if required and this.src[this.property] === undefined

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if ([null, undefined].includes(this.src[this.property]) {
   throw...
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or even just undefined

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

== null is already included null and undefined @veckatimest

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see. I just feel like we should avoid '==' at all costs (but it's up to you).

this.value = this.src[this.property] !== undefined ? this.src[this.property] : this.defaultValue

if (!this.required && this.value === undefined) {
return
}
if (!this.required && this.value === null) {
return this.value
this.target[this.property] = this.value
return
}

for (const executeValidation of this.conversions) {
Expand Down
129 changes: 126 additions & 3 deletions core/internal/arbimon/stream.int.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ beforeEach(async () => {
const PROJECT_1 = { id: 'testproj0001', name: 'Test project 1', externalId: 1, createdById: seedValues.primaryUserId }
const PROJECT_2 = { id: 'testproj0002', name: 'Test project 2', externalId: 2, createdById: seedValues.otherUserId }
const PROJECT_3 = { id: 'testproj0003', name: 'Test project 3', externalId: 3, createdById: seedValues.primaryUserId }
const PROJECTS = [PROJECT_1, PROJECT_2, PROJECT_3]
const PROJECT_4 = { id: 'testproj0004', name: 'Test project 4', externalId: 4, createdById: seedValues.primaryUserId }
const PROJECTS = [PROJECT_1, PROJECT_2, PROJECT_3, PROJECT_4]

const STREAM_1 = { id: 'LilSjZJkRK43', name: 'Stream 1', projectId: PROJECT_1.id, externalId: 1, createdById: seedValues.primaryUserId }
const STREAM_2 = { id: 'LilSjZJkRK46', name: 'Stream 2', projectId: PROJECT_2.id, externalId: 2, isPublic: true, createdById: seedValues.otherUserId }
const STREAMS = [STREAM_1, STREAM_2]
const STREAM_3 = { id: 'LilSjZJkRK49', name: 'Stream 3', projectId: PROJECT_4.id, externalId: 3, latitude: 10, longitude: 103, createdById: seedValues.primaryUserId, countryCode: 'TH', timezone: 'Asia/Bangkok' }
const STREAMS = [STREAM_1, STREAM_2, STREAM_3]

async function commonSetup () {
await models.Project.bulkCreate(PROJECTS)
Expand All @@ -54,6 +56,127 @@ describe('PATCH /internal/arbimon/streams/:externalId', () => {
expect(stream.name).toBe(body.name)
})

test('with [`name`] params should not change timezone and country code', async () => {
const body = {
name: 'New Stream 3'
}

const response = await request(app).patch(`/streams/${STREAM_3.externalId}`).send(body)

expect(response.statusCode).toBe(200)
const stream = await models.Stream.findByPk(STREAM_3.id)
expect(stream.name).toBe(body.name)
expect(stream.latitude).toBe(STREAM_3.latitude)
expect(stream.longitude).toBe(STREAM_3.longitude)
expect(stream.timezone).toBe(STREAM_3.timezone)
expect(stream.countryCode).toBe(STREAM_3.countryCode)
})

test('with [`name`, `latitude` = undefined, `longitude` = undefined] params should not change timezone and country code', async () => {
const body = {
name: 'New Stream 3',
latitude: undefined,
longitude: undefined
}

const response = await request(app).patch(`/streams/${STREAM_3.externalId}`).send(body)

expect(response.statusCode).toBe(200)
const stream = await models.Stream.findByPk(STREAM_3.id)
expect(stream.name).toBe(body.name)
expect(stream.latitude).toBe(STREAM_3.latitude)
expect(stream.longitude).toBe(STREAM_3.longitude)
expect(stream.timezone).toBe(STREAM_3.timezone)
expect(stream.countryCode).toBe(STREAM_3.countryCode)
})

test('with [`name`, `latitude` = 0, `longitude` = 0] params should change timezone and country code to default values', async () => {
const body = {
name: 'New Stream 3',
latitude: 0,
longitude: 0
}

const response = await request(app).patch(`/streams/${STREAM_3.externalId}`).send(body)

expect(response.statusCode).toBe(200)
const stream = await models.Stream.findByPk(STREAM_3.id)
expect(stream.name).toBe(body.name)
expect(stream.latitude).toBeNull()
expect(stream.longitude).toBeNull()
expect(stream.timezone).toBe('UTC')
expect(stream.countryCode).toBeNull()
})

test('with [`name`, `latitude`, `longitude` = null] params should change timezone and country code to default values', async () => {
const body = {
name: 'New Stream 3',
latitude: 10.1,
longitude: null
}

const response = await request(app).patch(`/streams/${STREAM_3.externalId}`).send(body)

expect(response.statusCode).toBe(200)
const stream = await models.Stream.findByPk(STREAM_3.id)
expect(stream.name).toBe(body.name)
expect(stream.latitude).toBe(body.latitude)
expect(stream.longitude).toBeNull()
expect(stream.timezone).toBe('UTC')
expect(stream.countryCode).toBeNull()
})

test('with [`name`, `latitude`= null, `longitude` = null] params should change timezone and country code to default values', async () => {
const body = {
name: 'New Stream 3',
latitude: null,
longitude: null
}

const response = await request(app).patch(`/streams/${STREAM_3.externalId}`).send(body)

expect(response.statusCode).toBe(200)
const stream = await models.Stream.findByPk(STREAM_3.id)
expect(stream.name).toBe(body.name)
expect(stream.latitude).toBeNull()
expect(stream.longitude).toBeNull()
expect(stream.timezone).toBe('UTC')
expect(stream.countryCode).toBeNull()
})

test('with [`name`, `latitude`, `longitude`] params should change timezone and country code correctly', async () => {
const body = {
name: 'New Stream 3',
longitude: 111
}
const mockCountry = jest.spyOn(googleMap, 'getCountry')
mockCountry.mockReturnValueOnce({
data: {
results: [{
address_components: [{
short_name: 'MY'
}]
}]
}
})
const mockTimezone = jest.spyOn(googleMap, 'getTimezone')
mockTimezone.mockReturnValueOnce({
data: {
timeZoneId: 'Asia/Myanmar'
}
})

const response = await request(app).patch(`/streams/${STREAM_3.externalId}`).send(body)

expect(response.statusCode).toBe(200)
const stream = await models.Stream.findByPk(STREAM_3.id)
expect(stream.name).toBe(body.name)
expect(stream.latitude).toBe(STREAM_3.latitude)
expect(stream.longitude).toBe(body.longitude)
expect(stream.timezone).toBe('Asia/Myanmar')
expect(stream.countryCode).toBe('MY')
})

test('with [`name`, `latitude`] params', async () => {
const body = {
name: 'New Stream 1',
Expand Down Expand Up @@ -195,7 +318,7 @@ describe('PATCH /internal/arbimon/streams/:externalId', () => {
const body = {
name: 'New Stream 1',
latitude: 19.9,
project_external_id: 4
project_external_id: 5
}

const response = await request(app).patch(`/streams/${STREAM_1.externalId}`).send(body)
Expand Down
16 changes: 15 additions & 1 deletion core/streams/dao/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,21 @@ async function update (id, data, options = {}) {
if (options.updatableBy && !(await hasPermission(UPDATE, options.updatableBy, id, STREAM))) {
throw new ForbiddenError()
}
const fullStream = { ...data, ...(await computedAdditions(data, stream)) }
// data.latitude or data.longitude is undefined mean it is not in the updated blob unlike null
// So need to assign the current stream latitude or longitude value
// If both are undefined mean there is no intention to update coordinate so no need to get additional data for timezone and country code
// Avoid change timezone and countryCode to default value
let additions = {}
if (data.latitude !== undefined || data.longitude !== undefined) {
veckatimest marked this conversation as resolved.
Show resolved Hide resolved
if (data.latitude === undefined) {
data.latitude = stream.latitude
}
if (data.longitude === undefined) {
data.longitude = stream.longitude
}
additions = await computedAdditions(data, stream)
}
const fullStream = { ...data, ...additions }
if (fullStream.name) {
if (stream && stream.project_id && stream.name !== fullStream.name) {
const duplicateStreamInProject = await query({ names: [fullStream.name], projects: [fullStream.project_id || stream.project_id] }, { fields: 'id', transaction })
Expand Down
Loading
Loading