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

Endpoint for claiming gateways using a qr code #7272

Merged
merged 6 commits into from
Sep 10, 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ For details about compatibility between different releases, see the **Commitment
- Option to filter out non-gateway related frequency plans.
- `ListFrequencyPlans` RPC has a new `gateways-only` flag.
- Option to pause application webhooks.
- Endpoint for claiming gateways using a qr code

### Changed

Expand Down
46 changes: 46 additions & 0 deletions api/ttn/lorawan/v3/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -743,10 +743,13 @@
- [Message `GetQRCodeFormatRequest`](#ttn.lorawan.v3.GetQRCodeFormatRequest)
- [Message `ParseEndDeviceQRCodeRequest`](#ttn.lorawan.v3.ParseEndDeviceQRCodeRequest)
- [Message `ParseEndDeviceQRCodeResponse`](#ttn.lorawan.v3.ParseEndDeviceQRCodeResponse)
- [Message `ParseGatewayQRCodeRequest`](#ttn.lorawan.v3.ParseGatewayQRCodeRequest)
- [Message `ParseGatewayQRCodeResponse`](#ttn.lorawan.v3.ParseGatewayQRCodeResponse)
- [Message `QRCodeFormat`](#ttn.lorawan.v3.QRCodeFormat)
- [Message `QRCodeFormats`](#ttn.lorawan.v3.QRCodeFormats)
- [Message `QRCodeFormats.FormatsEntry`](#ttn.lorawan.v3.QRCodeFormats.FormatsEntry)
- [Service `EndDeviceQRCodeGenerator`](#ttn.lorawan.v3.EndDeviceQRCodeGenerator)
- [Service `GatewayQRCodeGenerator`](#ttn.lorawan.v3.GatewayQRCodeGenerator)
- [File `ttn/lorawan/v3/regional.proto`](#ttn/lorawan/v3/regional.proto)
- [Message `ConcentratorConfig`](#ttn.lorawan.v3.ConcentratorConfig)
- [Message `ConcentratorConfig.Channel`](#ttn.lorawan.v3.ConcentratorConfig.Channel)
Expand Down Expand Up @@ -10542,6 +10545,34 @@ The Pba service allows clients to manage peering through Packet Broker.
| `format_id` | [`string`](#string) | | Identifier of the format used to successfully parse the QR code data. |
| `end_device_template` | [`EndDeviceTemplate`](#ttn.lorawan.v3.EndDeviceTemplate) | | |

### <a name="ttn.lorawan.v3.ParseGatewayQRCodeRequest">Message `ParseGatewayQRCodeRequest`</a>

| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `format_id` | [`string`](#string) | | QR code format identifier. If this field is not specified, the server will default to ttigpro1. |
| `qr_code` | [`bytes`](#bytes) | | Raw QR code contents. |

#### Field Rules

| Field | Validations |
| ----- | ----------- |
| `format_id` | <p>`string.max_len`: `36`</p><p>`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$`</p> |
| `qr_code` | <p>`bytes.min_len`: `10`</p><p>`bytes.max_len`: `1024`</p> |

### <a name="ttn.lorawan.v3.ParseGatewayQRCodeResponse">Message `ParseGatewayQRCodeResponse`</a>

| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `format_id` | [`string`](#string) | | Identifier of the format used to parse the QR code data. |
| `gateway_eui` | [`bytes`](#bytes) | | |
| `owner_token` | [`string`](#string) | | |

#### Field Rules

| Field | Validations |
| ----- | ----------- |
| `gateway_eui` | <p>`bytes.len`: `8`</p> |

### <a name="ttn.lorawan.v3.QRCodeFormat">Message `QRCodeFormat`</a>

| Field | Type | Label | Description |
Expand Down Expand Up @@ -10597,6 +10628,21 @@ The EndDeviceQRCodeGenerator service provides functionality to generate and pars
| `Parse` | `POST` | `/api/v3/qr-codes/end-devices/parse` | `*` |
| `Parse` | `POST` | `/api/v3/qr-codes/end-devices/{format_id}/parse` | `*` |

### <a name="ttn.lorawan.v3.GatewayQRCodeGenerator">Service `GatewayQRCodeGenerator`</a>

The GatewayQRCodeGenerator service provides functionality to generate and parse QR codes for gateways.

| Method Name | Request Type | Response Type | Description |
| ----------- | ------------ | ------------- | ------------|
| `Parse` | [`ParseGatewayQRCodeRequest`](#ttn.lorawan.v3.ParseGatewayQRCodeRequest) | [`ParseGatewayQRCodeResponse`](#ttn.lorawan.v3.ParseGatewayQRCodeResponse) | Parse QR Codes of known formats and return the information contained within. |

#### HTTP bindings

| Method Name | Method | Pattern | Body |
| ----------- | ------ | ------- | ---- |
| `Parse` | `POST` | `/api/v3/qr-codes/gateways/parse` | `*` |
| `Parse` | `POST` | `/api/v3/qr-codes/gateways/{format_id}/parse` | `*` |

## <a name="ttn/lorawan/v3/regional.proto">File `ttn/lorawan/v3/regional.proto`</a>

### <a name="ttn.lorawan.v3.ConcentratorConfig">Message `ConcentratorConfig`</a>
Expand Down
139 changes: 128 additions & 11 deletions api/ttn/lorawan/v3/api.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,9 @@
"name": "EndDeviceQRCodeGenerator",
"description": "Generate and parse end device QR codes."
},
{
"name": "GatewayQRCodeGenerator"
},
{
"name": "EntityRegistrySearch",
"description": "Search for entities in the Entity Registry."
Expand Down Expand Up @@ -13142,7 +13145,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/EndDeviceQRCodeGeneratorParseBody"
"$ref": "#/definitions/v3EndDeviceQRCodeGeneratorParseBody"
}
}
],
Expand All @@ -13151,6 +13154,79 @@
]
}
},
"/qr-codes/gateways/parse": {
"post": {
"summary": "Parse QR Codes of known formats and return the information contained within.",
"operationId": "GatewayQRCodeGenerator_Parse",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v3ParseGatewayQRCodeResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googlerpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/v3ParseGatewayQRCodeRequest"
}
}
],
"tags": [
"GatewayQRCodeGenerator"
]
}
},
"/qr-codes/gateways/{format_id}/parse": {
"post": {
"summary": "Parse QR Codes of known formats and return the information contained within.",
"operationId": "GatewayQRCodeGenerator_Parse2",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v3ParseGatewayQRCodeResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googlerpcStatus"
}
}
},
"parameters": [
{
"name": "format_id",
"description": "QR code format identifier.\nIf this field is not specified, the server will default to ttigpro1.",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/v3GatewayQRCodeGeneratorParseBody"
}
}
],
"tags": [
"GatewayQRCodeGenerator"
]
}
},
"/search/accounts": {
"get": {
"summary": "Search for accounts that match the conditions specified in the request.",
Expand Down Expand Up @@ -17413,16 +17489,6 @@
}
}
},
"EndDeviceQRCodeGeneratorParseBody": {
"type": "object",
"properties": {
"qr_code": {
"type": "string",
"format": "byte",
"description": "Raw QR code contents."
}
}
},
"EventAuthentication": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -23054,6 +23120,16 @@
}
}
},
"v3EndDeviceQRCodeGeneratorParseBody": {
"type": "object",
"properties": {
"qr_code": {
"type": "string",
"format": "byte",
"description": "Raw QR code contents."
}
}
},
"v3EndDeviceRegistryCreateBody": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -24281,6 +24357,16 @@
},
"description": "GatewayDown contains downlink messages for the gateway."
},
"v3GatewayQRCodeGeneratorParseBody": {
"type": "object",
"properties": {
"qr_code": {
"type": "string",
"format": "byte",
"description": "Raw QR code contents."
}
}
},
"v3GatewayRadio": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -27953,6 +28039,37 @@
}
}
},
"v3ParseGatewayQRCodeRequest": {
"type": "object",
"properties": {
"format_id": {
"type": "string",
"description": "QR code format identifier.\nIf this field is not specified, the server will default to ttigpro1."
},
"qr_code": {
"type": "string",
"format": "byte",
"description": "Raw QR code contents."
}
}
},
"v3ParseGatewayQRCodeResponse": {
"type": "object",
"properties": {
"format_id": {
"type": "string",
"description": "Identifier of the format used to parse the QR code data."
},
"gateway_eui": {
"type": "string",
"format": "string",
"example": "70B3D57ED000ABCD"
},
"owner_token": {
"type": "string"
}
}
},
"v3PayloadFormatter": {
"type": "string",
"enum": [
Expand Down
51 changes: 51 additions & 0 deletions api/ttn/lorawan/v3/qrcodegenerator.proto
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import "google/api/annotations.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/field_mask.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
import "thethings/json/annotations.proto";
import "ttn/lorawan/v3/end_device.proto";
import "ttn/lorawan/v3/picture.proto";
import "validate/validate.proto";
Expand Down Expand Up @@ -130,3 +131,53 @@ service EndDeviceQRCodeGenerator {
};
}
}

message ParseGatewayQRCodeRequest {
// QR code format identifier.
// If this field is not specified, the server will default to ttigpro1.
string format_id = 1 [(validate.rules).string = {
pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$",
max_len: 36
}];
// Raw QR code contents.
bytes qr_code = 2 [(validate.rules).bytes = {
min_len: 10,
max_len: 1024
}];
}

message ParseGatewayQRCodeResponse {
// Identifier of the format used to parse the QR code data.
string format_id = 1;
johanstokking marked this conversation as resolved.
Show resolved Hide resolved
bytes gateway_eui = 2 [
(validate.rules).bytes = {
len: 8,
ignore_empty: true
},
(thethings.json.field) = {
marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes",
unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes"
},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
type: STRING,
format: "string",
example: "\"70B3D57ED000ABCD\""
}
];
string owner_token = 3;
}

// The GatewayQRCodeGenerator service provides functionality to generate and parse QR codes for gateways.
service GatewayQRCodeGenerator {
// Parse QR Codes of known formats and return the information contained within.
rpc Parse(ParseGatewayQRCodeRequest) returns (ParseGatewayQRCodeResponse) {
option (google.api.http) = {
post: "/qr-codes/gateways/parse",
body: "*"
additional_bindings {
post: "/qr-codes/gateways/{format_id}/parse"
body: "*"
}
};
}
}
27 changes: 27 additions & 0 deletions config/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -9026,6 +9026,33 @@
"file": "enddevices.go"
}
},
"error:pkg/qrcodegenerator/qrcode/gateways:invalid_format": {
"translations": {
"en": "invalid format"
},
"description": {
"package": "pkg/qrcodegenerator/qrcode/gateways",
"file": "gateways.go"
}
},
"error:pkg/qrcodegenerator/qrcode/gateways:invalid_length": {
"translations": {
"en": "invalid length"
},
"description": {
"package": "pkg/qrcodegenerator/qrcode/gateways",
"file": "gateways.go"
}
},
"error:pkg/qrcodegenerator/qrcode/gateways:unknown_format": {
"translations": {
"en": "format unknown"
},
"description": {
"package": "pkg/qrcodegenerator/qrcode/gateways",
"file": "gateways.go"
}
},
"error:pkg/qrcodegenerator:format_not_found": {
"translations": {
"en": "format `{id}` not found"
Expand Down
2 changes: 1 addition & 1 deletion pkg/deviceclaimingserver/grpc_gateways_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ var (
authorizedCallOpt = grpc.PerRPCCredentials(authorizedMD)
)

func TestGatewayClaimingServer(t *testing.T) { // nolint:paralleltest
func TestGatewayClaimingServer(t *testing.T) { //nolint:paralleltest
a := assertions.New(t)
ctx := log.NewContext(test.Context(), test.GetLogger(t))
ctx, cancelCtx := context.WithCancel(ctx)
Expand Down
Loading
Loading