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

Device Support for JS SDK #278

Merged
merged 29 commits into from
Mar 27, 2019
Merged

Conversation

kschiffer
Copy link
Contributor

@kschiffer kschiffer commented Mar 11, 2019

Summary:
Closes #131.
This PR adds the first implementation for supporting device crud in the SDK. It follows the logic of the cli commands for doing device CRUD, including merging of entity parts from different components, based on the field mask paths.
It also simplifies the SDK implementation and streamlines marshaling of field masks and unwrapping of api responses, along with other improvements.

Changes:

  • Decorating the device service
    • Handling device entities
    • proper request logic (resolving requests to component endpoints based on responsibility)
    • merging entities (combining parts of the end device structure coming from different components)
    • device creation
      • settings defaults based on flags (setDefaults, abp/otaa)
      • rolling back fragmented entries when device creation fails
    • updating devices
    • deleting devices (throughout all components or selective)
    • listing devices (entity registry only)
  • marshaling of field masks from path strings or arrays (called selectors)
  • component responsibility map for devices in get and set/update
  • removal of withId() shorthand
  • simplification of entity transformation logic (proxied or not)
  • improving request parameter logic (passing fieldmasks, query and route parameters explicitly to the api handler)

Notes for Reviewers:

  • due to timing issues, I have skipped unit tests for now
  • I submit this as a draft PR so you can start reviewing, but I still have to integrate this branch to the console API to check for any issues, which I think makes sense to do before merging this
  • I have, however, obviously checked the new functions to be operative using test scripts

Release Notes: (optional)
Added Device support to JS SDK.

@kschiffer kschiffer added the c/sdk/js This is related to the JavaScript SDK label Mar 11, 2019
@kschiffer kschiffer added this to the March 2019 milestone Mar 11, 2019
@kschiffer kschiffer self-assigned this Mar 11, 2019
@kschiffer kschiffer requested a review from bafonins March 11, 2019 16:11
@kschiffer
Copy link
Contributor Author

Concerning the removal of withId(): I think it is not helpful in its current shape and adds an unnecessary layer. For us, it is easier to just access the sub-services directly, manually passing the parent entity ids, which is perfectly fine for the time being.

@@ -82,12 +90,51 @@ class Marshaler {
return this.payloadSingleResponse(result, transform)
}

static unwrapDevices (result, transform) {
return this.payloadListResponse(result, transform)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
return this.payloadListResponse(result, transform)
return this.payloadListResponse('devices', result, transform)

you are passing the transform function instead of the result object to payloadListResponse


const routeParams = params.route || {}
const queryParams = params.query || {}
this[serviceName][rpcName] = function ({ routeParams = {}, queryParams = {}, fieldMask = []}, payload) {
Copy link
Contributor

Choose a reason for hiding this comment

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

why would you pass the field mask separately?
i expect it to be in the payload already in delete/put/post requests and in the query in get requests

Copy link
Contributor Author

@kschiffer kschiffer Mar 13, 2019

Choose a reason for hiding this comment

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

Exactly because of this distinction between GET and POST/PATCH/etc. Before we appended a querified object directly to the url, but axios actually provides an API to do this via passingconfig.params to the request method. We should use that because it is directly integrated and also saves us a dependency (query-string).
I was detaching the field mask from the payload then to simplify the logic of passing this to get/non-get (either via config.params or payload) correctly.
But yeah, looking at it again, we can likely simplify this by treating it as part of the payload again and passing the payload as config.params in case of GET.


return connector[endpoint.method](route, body)
return connector[endpoint.method](route, payload, { queryParams, fieldMask })
Copy link
Contributor

Choose a reason for hiding this comment

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

arent query parameters only for get requests?

Copy link
Contributor Author

@kschiffer kschiffer Mar 13, 2019

Choose a reason for hiding this comment

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

No, they can be used always. Though we probably won't face that in the API. As such I will remove them – makes things easier as well.

import Link from './link'
import DeviceService from './devices'

Copy link
Contributor

Choose a reason for hiding this comment

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

redundant new line

'application.ids.application_id': id,
},
async updateById (id, patch, selector) {
const fieldMask = selector
Copy link
Contributor

Choose a reason for hiding this comment

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

why? do you change anything in the Application service? this doesnt seem to be related to devices

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed. This is not necessary and I will remove it. However, we actually need to change the getById method to accept selectors (as intermediate to field masks), otherwise, we won't get e.g. name or description field by default, which is the case at the moment. I would add that in this PR as well, as it is a small change.


import crypto from 'crypto'

export default function randomByteString (len, type = 'hex') {
Copy link
Contributor

Choose a reason for hiding this comment

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

so this is just a stub?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is following the cli command:
https://github.com/TheThingsNetwork/lorawan-stack/blob/master/cmd/ttn-lw-cli/commands/end_devices.go#L101-L105

I think this will be enhanced at a later stage.

const component = this._parseStackComponent(endpoint)
try {
return await this[component][method](endpoint, payload)
if (method === 'get') {
return await this[component][method](endpoint, config)
Copy link
Contributor

Choose a reason for hiding this comment

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

why not passing an empty payload here?
why do we need that separation?

Copy link
Contributor Author

@kschiffer kschiffer Mar 13, 2019

Choose a reason for hiding this comment

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

To me, it seemed weird to define a get function with a payload argument that is always discarded.

Copy link
Contributor Author

@kschiffer kschiffer Mar 13, 2019

Choose a reason for hiding this comment

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

Disregard the above, I actually mixed that up. You are right indeed. I wrongfully thought, the different signature of the get method would make that necessary.

After trying it out, it actually is necessary when using the method aliases because get() doesn't accept more than two arguments. We can still get rid of the if check by using the plain axios function.

@kschiffer kschiffer force-pushed the feature/131-js-sdk-device-support branch 3 times, most recently from 6ffbc7d to 4f40c96 Compare March 13, 2019 16:13
@kschiffer kschiffer requested a review from bafonins March 14, 2019 12:32
@kschiffer kschiffer force-pushed the feature/131-js-sdk-device-support branch from 4f40c96 to 7086da0 Compare March 19, 2019 12:15
@kschiffer kschiffer marked this pull request as ready for review March 25, 2019 08:23
Copy link
Contributor

@bafonins bafonins left a comment

Choose a reason for hiding this comment

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

This branch breaks several pieces of functionality of the console, for example, linking, editing application data.
Error:

{"code":3,"message":"json: cannot unmarshal array into Go value of type map[string]json.RawMessage"}

import Application from '../entity/application'
import ApiKeys from './api-keys'
import ApiKeysService from './api-keys'
Copy link
Contributor

Choose a reason for hiding this comment

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

why?

import Link from './link'
import Device from './devices'
Copy link
Contributor

Choose a reason for hiding this comment

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

inconsistent naming. DeviceService, Device, DevicesService, Devices?

@kschiffer
Copy link
Contributor Author

This branch breaks several pieces of functionality of the console, for example, linking, editing application data.

Right. We need another merge strategy here, as merging to master would break the console. This is mostly because of slight changes in the signatures of the service functions. I will follow up with a description of these changes tomorrow.
As this PR is scoped to the SDK, I would like to avoid bloating this PR more with additional console changes.
My proposition is to merge this branch into another branch, on top of which I will then open another PR that will address the compatibility with the console, so that we can merge safely into master and treat this PR as isolated SDK change.

@kschiffer kschiffer changed the base branch from master to feature/131-sdk-console-compatibility March 25, 2019 16:11
@kschiffer kschiffer force-pushed the feature/131-js-sdk-device-support branch from 7086da0 to 0afcc01 Compare March 26, 2019 10:58
@kschiffer
Copy link
Contributor Author

kschiffer commented Mar 26, 2019

So after checking this, there aren't actually breaking changes, as the new function signatures are only extending the argument list and provide defaults. The errors you experienced were caused by faulty handling of field masks, which I have fixed now.

@kschiffer kschiffer changed the base branch from feature/131-sdk-console-compatibility to master March 26, 2019 11:03
@kschiffer kschiffer force-pushed the feature/131-js-sdk-device-support branch from 0afcc01 to 00d7381 Compare March 26, 2019 12:32
@kschiffer kschiffer force-pushed the feature/131-js-sdk-device-support branch from 00d7381 to 40b4db5 Compare March 26, 2019 12:57
@kschiffer kschiffer force-pushed the feature/131-js-sdk-device-support branch from 40b4db5 to a5a2050 Compare March 26, 2019 13:01
@kschiffer kschiffer merged commit b15d31f into master Mar 27, 2019
@kschiffer kschiffer deleted the feature/131-js-sdk-device-support branch March 27, 2019 08:03
This was referenced Apr 29, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c/sdk/js This is related to the JavaScript SDK
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants