diff --git a/.travis.yml b/.travis.yml index 4b30d1e..5a4d82f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: go go: - 1.13.x -- 1.14.1 +- 1.14.4 install: - go get -t ./... - ./scripts/install-checks.sh diff --git a/docker/Dockerfile b/docker/Dockerfile index 387fc93..1e404c0 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.14.1-alpine3.11 as builder +FROM golang:1.14.4-alpine3.12 as builder LABEL maintainer="Samuel Jirenius " diff --git a/docker/Dockerfile.alpine b/docker/Dockerfile.alpine index 50bab48..3cbd41a 100644 --- a/docker/Dockerfile.alpine +++ b/docker/Dockerfile.alpine @@ -1,4 +1,4 @@ -FROM golang:1.14.1-alpine3.11 as builder +FROM golang:1.14.4-alpine3.12 as builder LABEL maintainer="Samuel Jirenius " @@ -15,7 +15,7 @@ COPY . . RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -v -ldflags "-s -w" -o /resgate -FROM alpine:3.11 +FROM alpine:3.12 COPY --from=builder /resgate /bin/resgate EXPOSE 8080 diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index f3680f6..f4f9202 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -2,6 +2,12 @@ All changes to the RES Protocol will be documented in this file. +## v1.2.1 - [Resgate v1.6.0](compare/v1.4.0...v1.6.0) - 2020-06-15 + +* #157 Soft resource references. +* #161 Unsubscribe count field. +* #165 Data values. + ## v1.2.0 - [Resgate v1.4.0](compare/v1.3.0...v1.4.0) - 2019-11-20 * #127 Resource response on query request. diff --git a/docs/res-client-protocol.md b/docs/res-client-protocol.md index 4a2f8f1..7815341 100644 --- a/docs/res-client-protocol.md +++ b/docs/res-client-protocol.md @@ -1,6 +1,6 @@ # The RES-Client Protocol Specification -*Version: [1.2.0](res-protocol-semver.md)* +*Version: [1.2.1](res-protocol-semver.md)* ## Table of contents - [Introduction](#introduction) @@ -40,17 +40,17 @@ The RES-Client protocol is used in communication between the client and the gate A core concept in the RES-Client protocol is the subscriptions. A client may subscribe to resources by making [subscribe requests](#subscribe-request) with the unique [resource ID](res-protocol.md#resource-ids), or by getting a resource response on a [call request](#call-request) or [auth request](#auth-request). -A resource may be subscribed to [directly](#direct-subscription) or [indirectly](#indirect-subscription). Any reference to *subscription*, or a resource being *subscribed* to, in this document should be interpreted as both *direct* and *indirect* subscriptions, unless specified. +A resource may be subscribed to [directly](#direct-subscription) or [indirectly](#indirect-subscription). Any reference in this document to *subscription* or a resource being *subscribed* to, should be interpreted as both *direct* and *indirect* subscriptions, unless specified. The client will receive [events](#events) on anything that happens on a subscribed resource. A subscription lasts as long as the resource has direct or indirect subscriptions, or when the connection to the gateway is closed. ## Direct subscription The resource that is subscribed to with a [subscribe request](#subscribe-request), or returned as a resource response to a [call request](#call-request) or [auth request](#auth-request) will be considered *directly subscribed*. -It is possible to make multiple direct subscriptions on a resource. It will be considered directly subscribed until an equal number of [unsubscribe requests](#unsubscribe-request) has been made. +It is possible to have multiple direct subscriptions on a resource. It will be considered directly subscribed until the same number of subscriptions are matched using one ore more [unsubscribe requests](#unsubscribe-request). ## Indirect subscription -A resource that is referred to with a [resource reference](res-protocol.md#values) by a [directly subscribed](#direct-subscription) resource, or by an indirectly subscribed resource, will be considered *indirectly subscribed*. Cyclic references where none of the resources are directly subscribed will not be considered subscribed. +A resource that is referred to with a non-soft [resource reference](res-protocol.md#resource-references) by a [directly subscribed](#direct-subscription) resource, or by an indirectly subscribed resource, will be considered *indirectly subscribed*. Cyclic references where none of the resources are directly subscribed will not be considered subscribed. ## Resource set @@ -231,26 +231,33 @@ May be omitted if no subscribed resources encountered errors. ### Error An error response will be sent if the resource couldn't be subscribed to. -Any [resource reference](res-protocol.md#values) that fails will not lead to an error response, but the error will be added to the [resource set](#resource-set) errors. +Any [resource reference](res-protocol.md#resource-references) that fails will not lead to an error response, but the error will be added to the [resource set](#resource-set) errors. ## Unsubscribe request -Unsubscribe requests are sent by the client to unsubscribe to a previous made [direct subscription](#direct-subscription). +Unsubscribe requests are sent by the client to unsubscribe to previous [direct subscriptions](#direct-subscription). The resource will only be considered unsubscribed when there are no more [direct](#direct-subscription) or [indirect](#indirect-subscription) subscriptions. +If the **count** property is omitted in the request, the value of 1 is assumed. + **method** `unsubscribe.` ### Parameters -The request has no parameters. +The request parameters are optional. +It not omitted, the parameters object SHOULD have the following property: + +**count** +The number of direct subscriptions to unsubscribe to. +MUST be a number greater than 0. ### Result The result has no payload. ### Error -An error response with code `system.noSubscription` will be sent if the resource has no direct subscription. +An error response with code `system.noSubscription` will be sent if the resource has no direct subscription, or if *count* exceeds the number of direct subscriptions. If so, the number of direct subscriptions will be unaffected. ## Get request @@ -280,7 +287,7 @@ May be omitted if no retrieved resources encountered errors. ### Error An error response will be sent if the resource couldn't be retrieved. -Any [resource reference](res-protocol.md#values) that fails will not lead to an error response, but the error will be added to the [resource set](#resource-set) errors. +Any [resource reference](res-protocol.md#resource-references) that fails will not lead to an error response, but the error will be added to the [resource set](#resource-set) errors. ## Call request @@ -420,7 +427,7 @@ Event data. The payload is defined by the event type. ## Model change event Change events are sent when a [model](res-protocol.md#models)'s properties has been changed. -Will result in new [indirect subscriptions](#indirect-subscription) if changed properties contain [resource references](res-protocol.md#values) previously not subscribed. +Will result in new [indirect subscriptions](#indirect-subscription) if changed properties contain [resource references](res-protocol.md#resource-references) previously not subscribed. Change events are only sent on [models](res-protocol.md#models). **event** @@ -467,7 +474,7 @@ A delete action is a JSON object used when a property has been deleted from a mo ## Collection add event Add events are sent when a value is added to a [collection](res-protocol.md#collections). -Will result in one or more new [indirect subscriptions](#indirect-subscription) if added value is a [resource references](res-protocol.md#values) previously not subscribed. +Will result in one or more new [indirect subscriptions](#indirect-subscription) if added value is a [resource references](res-protocol.md#resource-references) previously not subscribed. Add events are only sent on [collections](res-protocol.md#collections). **event** diff --git a/docs/res-protocol-semver.md b/docs/res-protocol-semver.md index 8393427..10a6ae9 100644 --- a/docs/res-protocol-semver.md +++ b/docs/res-protocol-semver.md @@ -25,3 +25,4 @@ New patch versions may include features like: * The current RES Protocol version is stated at the top of the [RES Protocol Specification](res-protocol.md) document. * The same version number is used for both the [RES Service protocol](res-service-protocol.md) and [RES Client protocol](res-client-protocol.md), even if some version changes only affect one part of the protocol. * Language updates, clarifications, or added examples, does not justify a version change. +* Unreleased versions are given a preliminary PATCH version, which may change to a MINOR version on release. diff --git a/docs/res-protocol.md b/docs/res-protocol.md index f2cd9d7..b9ed1d8 100644 --- a/docs/res-protocol.md +++ b/docs/res-protocol.md @@ -1,6 +1,6 @@ # RES Protocol -*Version: [1.2.0](res-protocol-semver.md)* +*Version: [1.2.1](res-protocol-semver.md)* ## Table of contents - [Introduction](#introduction) @@ -79,27 +79,48 @@ A collection is an ordered list of [values](#values) represented by a JSON array ## Values -A value is either a *primitive* or a [resource reference](#resource-references). -A primitive is either a JSON `string`, `number`, `true`, `false`, or `null` value. +A value is either a [primitive](#primitives), a [resource reference](#resource-references), or a [data value](#data-values). ### Example ```javascript -"foo" // string -42 // number -true // boolean true -false // boolean false -null // null -{ "rid": "example.user.42" } // resource reference +"foo" // string +42 // number +true // boolean true +false // boolean false +null // null + +{ "rid": "example.user.42" } // resource reference +{ "rid": "example.page.2", "soft":true } // soft reference +{ "data": { "foo": [ "bar" ] }} // data value +{ "data": 42 } // data value interchangeable with the primitive 42 ``` +## Primitives + +A primitive is either a JSON `string`, `number`, `true`, `false`, or `null` value. + ## Resource references -A resource reference is a JSON objects with the following parameter: +A resource reference is a link to a resource. A *soft reference* is a resource reference which will not automatically be followed by the gateway. The resource reference is a JSON object with the following parameters: **rid** Resource ID of the referenced resource. MUST be a valid [resource ID](#resource-ids). +**soft** +Flag telling if the reference is a soft resource reference. +May be omitted if the reference is not a soft reference. +MUST be a boolean. + +## Data values + +A data value contains any JSON value, including nested objects and arrays. +If the contained value can be expressed as a [primitive](#primitives), the data value is interchangeable with the primitive. +The data value is a JSON object with the following parameter: + +**data** +The contained JSON value. + ## Messaging system The messaging system handles the communication between [services](#services) and [gateways](#gateways). It MUST provide the following functionality: diff --git a/docs/res-service-protocol.md b/docs/res-service-protocol.md index 17dc038..b42c662 100644 --- a/docs/res-service-protocol.md +++ b/docs/res-service-protocol.md @@ -1,6 +1,6 @@ # The RES-Service Protocol Specification -*Version: [1.2.0](res-protocol-semver.md)* +*Version: [1.2.1](res-protocol-semver.md)* ## Table of contents - [Introduction](#introduction) @@ -152,14 +152,16 @@ MUST be a string. ### Result **get** -Flag if the client has access to get (read) the resource. +Flag telling if the client has access to get (read) the resource, including any +resource recursively referenced by non-soft [resource +references](res-protocol.md#resource-references). May be omitted if client has no get access. -MUST be a boolean +MUST be a boolean. **call** A comma separated list of methods that the client can call. Eg. `"set,foo,bar"`. May be omitted if client is not allowed to call any methods. -Value may be a single asterisk character (`"*"`) if client is allowed to call any method. +Value may be a single asterisk character (`"*"`) if client is allowed to call any method. ### Error @@ -343,7 +345,7 @@ For new models, the parameters SHOULD be an object containing the named properti For new collections, the parameters SHOULD be an ordered array containing the [values](res-protocol.md#values) of the collection. **Result** -MUST be a [resource reference](res-protocol.md#values) to the new resource. +MUST be a [resource reference](res-protocol.md#resource-references) to the new resource. # Events @@ -378,6 +380,7 @@ The event payload has the following parameter: **values** A key/value object describing the properties that was changed. Each property should have a new [value](res-protocol.md#values) or a [delete action](#delete-action). +For changes in [data values](res-protocol.md#data-values), the value is changed in its entirety. Unchanged properties SHOULD NOT be included. **Example payload** @@ -404,6 +407,7 @@ A delete action is a JSON object used when a property has been deleted from a mo Add events are sent when a value is added to a [collection](res-protocol.md#collections). Any previous value at the same index or higher will implicitly be shifted one step to a higher index. MUST NOT be sent on [models](res-protocol.md#models). +Values cannot be added to arrays inside [data values](res-protocol.md#data-values), but the data value must be changed in its entirety. The event payload has the following parameters: **value** @@ -429,6 +433,7 @@ MUST be a number that is zero or greater and less than or equal to the length of Remove events are sent when a value is removed from a [collection](res-protocol.md#collections). Any previous value at a higher index will implicitly be shifted one step to a lower index. MUST NOT be sent on [models](res-protocol.md#models). +Values cannot be removed from arrays inside [data values](res-protocol.md#data-values), but the data value must be changed in its entirety. The event payload has the following parameter: **idx** diff --git a/examples/book-collection/package-lock.json b/examples/book-collection/package-lock.json index bfc29ce..ac92185 100644 --- a/examples/book-collection/package-lock.json +++ b/examples/book-collection/package-lock.json @@ -99,18 +99,18 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "nats": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/nats/-/nats-1.3.0.tgz", - "integrity": "sha512-0Rbtl0WV5kXLcZlE5fbX06/YwVzvx9Ui5xaIsVH6rzRGu7FRUAHxueaZBPHIN3M82s3N4bbkSwghYipgcsIgbA==", + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/nats/-/nats-1.4.9.tgz", + "integrity": "sha512-D0qTg2v0jir32TCJfGkxRwOba4/9vqJJmr4GtghR/J9AF6E0y1BdCeXxnD2G+hruSPnKUbF0NzaQVPbSg5fdOg==", "requires": { - "nuid": "^1.0.0", - "ts-nkeys": "^1.0.8" + "nuid": "^1.1.4", + "ts-nkeys": "^1.0.16" } }, "nuid": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/nuid/-/nuid-1.1.0.tgz", - "integrity": "sha512-C/JdZ6PtCqKsCEs4ni76nhBsdmuQgLAT/CTLNprkcLViDAnkk7qx5sSA8PVC2vmSsdBlSsFuGb52v6pwn1oaeg==" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/nuid/-/nuid-1.1.4.tgz", + "integrity": "sha512-PXiYyHhGfrq8H4g5HyC8enO1lz6SBe5z6x1yx/JG4tmADzDGJVQy3l1sRf3VtEvPsN8dGn9hRFRwDKWL62x0BA==" }, "on-finished": { "version": "2.3.0", @@ -184,17 +184,17 @@ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, "ts-nkeys": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/ts-nkeys/-/ts-nkeys-1.0.12.tgz", - "integrity": "sha512-5TgA+wbfxTy/9pdSuAhvneuL65KKoI7phonzNQH2UhnorAQAWehUwHNLEuli596wu/Fxh0SAhMeKZVLNx4s7Ow==", + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/ts-nkeys/-/ts-nkeys-1.0.16.tgz", + "integrity": "sha512-1qrhAlavbm36wtW+7NtKOgxpzl+70NTF8xlz9mEhiA5zHMlMxjj3sEVKWm3pGZhHXE0Q3ykjrj+OSRVaYw+Dqg==", "requires": { - "tweetnacl": "^1.0.1" + "tweetnacl": "^1.0.3" } }, "tweetnacl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.1.tgz", - "integrity": "sha512-kcoMoKTPYnoeS50tzoqjPY3Uv9axeuuFAZY9M/9zFnhoVvRfxz9K29IMPD7jGmt2c8SW7i3gT9WqDl2+nV7p4A==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" }, "unpipe": { "version": "1.0.0", diff --git a/examples/book-collection/package.json b/examples/book-collection/package.json index bd32646..67c357c 100644 --- a/examples/book-collection/package.json +++ b/examples/book-collection/package.json @@ -3,7 +3,7 @@ "description": "Resgate Book Collection Example", "dependencies": { "connect": "^3.7.0", - "nats": "^1.3.0", + "nats": "^1.4.9", "serve-static": "^1.14.1" }, "devDependencies": {}, diff --git a/examples/client-session/package-lock.json b/examples/client-session/package-lock.json index 8c48455..2e56f69 100644 --- a/examples/client-session/package-lock.json +++ b/examples/client-session/package-lock.json @@ -187,9 +187,9 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ipaddr.js": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", - "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, "media-typer": { "version": "0.3.0", @@ -212,16 +212,16 @@ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" }, "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", "requires": { - "mime-db": "1.40.0" + "mime-db": "1.44.0" } }, "ms": { @@ -230,12 +230,12 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "nats": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/nats/-/nats-1.3.0.tgz", - "integrity": "sha512-0Rbtl0WV5kXLcZlE5fbX06/YwVzvx9Ui5xaIsVH6rzRGu7FRUAHxueaZBPHIN3M82s3N4bbkSwghYipgcsIgbA==", + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/nats/-/nats-1.4.9.tgz", + "integrity": "sha512-D0qTg2v0jir32TCJfGkxRwOba4/9vqJJmr4GtghR/J9AF6E0y1BdCeXxnD2G+hruSPnKUbF0NzaQVPbSg5fdOg==", "requires": { - "nuid": "^1.0.0", - "ts-nkeys": "^1.0.8" + "nuid": "^1.1.4", + "ts-nkeys": "^1.0.16" } }, "negotiator": { @@ -244,9 +244,9 @@ "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, "nuid": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/nuid/-/nuid-1.1.0.tgz", - "integrity": "sha512-C/JdZ6PtCqKsCEs4ni76nhBsdmuQgLAT/CTLNprkcLViDAnkk7qx5sSA8PVC2vmSsdBlSsFuGb52v6pwn1oaeg==" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/nuid/-/nuid-1.1.4.tgz", + "integrity": "sha512-PXiYyHhGfrq8H4g5HyC8enO1lz6SBe5z6x1yx/JG4tmADzDGJVQy3l1sRf3VtEvPsN8dGn9hRFRwDKWL62x0BA==" }, "on-finished": { "version": "2.3.0", @@ -267,12 +267,12 @@ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, "proxy-addr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", - "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", "requires": { "forwarded": "~0.1.2", - "ipaddr.js": "1.9.0" + "ipaddr.js": "1.9.1" } }, "qs": { @@ -360,17 +360,17 @@ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, "ts-nkeys": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/ts-nkeys/-/ts-nkeys-1.0.12.tgz", - "integrity": "sha512-5TgA+wbfxTy/9pdSuAhvneuL65KKoI7phonzNQH2UhnorAQAWehUwHNLEuli596wu/Fxh0SAhMeKZVLNx4s7Ow==", + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/ts-nkeys/-/ts-nkeys-1.0.16.tgz", + "integrity": "sha512-1qrhAlavbm36wtW+7NtKOgxpzl+70NTF8xlz9mEhiA5zHMlMxjj3sEVKWm3pGZhHXE0Q3ykjrj+OSRVaYw+Dqg==", "requires": { - "tweetnacl": "^1.0.1" + "tweetnacl": "^1.0.3" } }, "tweetnacl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.1.tgz", - "integrity": "sha512-kcoMoKTPYnoeS50tzoqjPY3Uv9axeuuFAZY9M/9zFnhoVvRfxz9K29IMPD7jGmt2c8SW7i3gT9WqDl2+nV7p4A==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" }, "type-is": { "version": "1.6.18", diff --git a/examples/client-session/package.json b/examples/client-session/package.json index bc78427..39d28e8 100644 --- a/examples/client-session/package.json +++ b/examples/client-session/package.json @@ -3,7 +3,7 @@ "description": "Resgate Client Session Example", "dependencies": { "express": "^4.17.1", - "nats": "^1.3.0" + "nats": "^1.4.9" }, "scripts": { "start": "node server.js" diff --git a/examples/edit-text/package-lock.json b/examples/edit-text/package-lock.json index c4a15ea..81bb947 100644 --- a/examples/edit-text/package-lock.json +++ b/examples/edit-text/package-lock.json @@ -10,13 +10,13 @@ "dev": true }, "accepts": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", - "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", "dev": true, "requires": { - "mime-types": "~2.1.18", - "negotiator": "0.6.1" + "mime-types": "~2.1.24", + "negotiator": "0.6.2" } }, "ajv": { @@ -38,12 +38,39 @@ "dev": true, "requires": { "string-width": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } } }, "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, "ansi-styles": { @@ -56,9 +83,9 @@ } }, "arch": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.1.1.tgz", - "integrity": "sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.1.2.tgz", + "integrity": "sha512-NTBIIbAfkJeIletyABbVtdPgeKfDafR+1mZV/AyyfC1UkVkp9iUjV+wwmqtUgphHYajbI86jejBJp5e+jkGTiQ==", "dev": true }, "arg": { @@ -88,11 +115,36 @@ "widest-line": "^2.0.0" }, "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, "camelcase": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } } } }, @@ -156,17 +208,6 @@ "execa": "^0.8.0" }, "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, "execa": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/execa/-/execa-0.8.0.tgz", @@ -181,32 +222,20 @@ "signal-exit": "^3.0.0", "strip-eof": "^1.0.0" } - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true } } }, "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", "dev": true, "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" } }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -223,12 +252,12 @@ "dev": true }, "compressible": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.16.tgz", - "integrity": "sha512-JQfEOdnI7dASwCuSPWIeVYwc/zMsu/+tRhoUvEfXz2gxOA2DNjmG5vhtFdBlhWPPGo+RdT9S3tgc/uH5qgDiiA==", + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", "dev": true, "requires": { - "mime-db": ">= 1.38.0 < 2" + "mime-db": ">= 1.43.0 < 2" } }, "compression": { @@ -253,20 +282,20 @@ "dev": true }, "concurrently": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-4.1.1.tgz", - "integrity": "sha512-48+FE5RJ0qc8azwKv4keVQWlni1hZeSjcWr8shBelOBtBHcKj1aJFM9lHRiSc1x7lq416pkvsqfBMhSRja+Lhw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-5.2.0.tgz", + "integrity": "sha512-XxcDbQ4/43d6CxR7+iV8IZXhur4KbmEJk1CetVMUqCy34z9l0DkszbY+/9wvmSnToTej0SYomc2WSRH+L0zVJw==", "dev": true, "requires": { - "chalk": "^2.4.1", - "date-fns": "^1.23.0", - "lodash": "^4.17.10", + "chalk": "^2.4.2", + "date-fns": "^2.0.1", + "lodash": "^4.17.15", "read-pkg": "^4.0.1", - "rxjs": "^6.3.3", + "rxjs": "^6.5.2", "spawn-command": "^0.0.2-1", - "supports-color": "^4.5.0", - "tree-kill": "^1.1.0", - "yargs": "^12.0.1" + "supports-color": "^6.1.0", + "tree-kill": "^1.2.2", + "yargs": "^13.3.0" } }, "content-disposition": { @@ -276,22 +305,20 @@ "dev": true }, "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", + "lru-cache": "^4.0.1", "shebang-command": "^1.2.0", "which": "^1.2.9" } }, "date-fns": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", - "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.14.0.tgz", + "integrity": "sha512-1zD+68jhFgDIM0rF05rcwYO8cExdNqxjq4xP1QKM60Q45mnO6zaMWB4tOzrIr4M4GSLntsKeE4c9Bdl2jhL/yw==", "dev": true }, "debug": { @@ -315,14 +342,11 @@ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true }, "error-ex": { "version": "1.3.2", @@ -340,13 +364,13 @@ "dev": true }, "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", "is-stream": "^1.1.0", "npm-run-path": "^2.0.0", "p-finally": "^1.0.0", @@ -361,9 +385,9 @@ "dev": true }, "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, "fast-url-parser": { @@ -393,19 +417,16 @@ } }, "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true }, "has-flag": { "version": "3.0.0", @@ -414,9 +435,9 @@ "dev": true }, "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", "dev": true }, "ini": { @@ -425,12 +446,6 @@ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -467,15 +482,6 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -502,47 +508,21 @@ "yallist": "^2.1.2" } }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, - "requires": { - "p-defer": "^1.0.0" - } - }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, "mime-db": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", - "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==", + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", "dev": true }, "mime-types": { - "version": "2.1.22", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", - "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", "dev": true, "requires": { - "mime-db": "~1.38.0" + "mime-db": "1.44.0" } }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -565,24 +545,18 @@ "dev": true }, "nats": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/nats/-/nats-1.3.0.tgz", - "integrity": "sha512-0Rbtl0WV5kXLcZlE5fbX06/YwVzvx9Ui5xaIsVH6rzRGu7FRUAHxueaZBPHIN3M82s3N4bbkSwghYipgcsIgbA==", + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/nats/-/nats-1.4.9.tgz", + "integrity": "sha512-D0qTg2v0jir32TCJfGkxRwOba4/9vqJJmr4GtghR/J9AF6E0y1BdCeXxnD2G+hruSPnKUbF0NzaQVPbSg5fdOg==", "requires": { - "nuid": "^1.0.0", - "ts-nkeys": "^1.0.8" + "nuid": "^1.1.4", + "ts-nkeys": "^1.0.16" } }, "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", "dev": true }, "normalize-package-data": { @@ -607,15 +581,9 @@ } }, "nuid": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/nuid/-/nuid-1.1.0.tgz", - "integrity": "sha512-C/JdZ6PtCqKsCEs4ni76nhBsdmuQgLAT/CTLNprkcLViDAnkk7qx5sSA8PVC2vmSsdBlSsFuGb52v6pwn1oaeg==" - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/nuid/-/nuid-1.1.4.tgz", + "integrity": "sha512-PXiYyHhGfrq8H4g5HyC8enO1lz6SBe5z6x1yx/JG4tmADzDGJVQy3l1sRf3VtEvPsN8dGn9hRFRwDKWL62x0BA==" }, "on-headers": { "version": "1.0.2", @@ -623,48 +591,16 @@ "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", "dev": true }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true - }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", "dev": true }, - "p-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", - "dev": true - }, "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -737,16 +673,6 @@ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -808,24 +734,24 @@ "dev": true }, "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, "resolve": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz", - "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", "dev": true, "requires": { "path-parse": "^1.0.6" } }, "rxjs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz", - "integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==", + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", + "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", "dev": true, "requires": { "tslib": "^1.9.0" @@ -838,9 +764,9 @@ "dev": true }, "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, "serve": { @@ -937,9 +863,9 @@ "dev": true }, "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, "spawn-command": { @@ -949,9 +875,9 @@ "dev": true }, "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", @@ -959,15 +885,15 @@ } }, "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", "dev": true }, "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, "requires": { "spdx-exceptions": "^2.1.0", @@ -981,22 +907,23 @@ "dev": true }, "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { + "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "strip-ansi": "^5.1.0" } }, "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "^4.1.0" } }, "strip-eof": { @@ -1012,20 +939,12 @@ "dev": true }, "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "requires": { - "has-flag": "^2.0.0" - }, - "dependencies": { - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - } + "has-flag": "^3.0.0" } }, "term-size": { @@ -1035,66 +954,32 @@ "dev": true, "requires": { "execa": "^0.7.0" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - } } }, "tree-kill": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.1.tgz", - "integrity": "sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true }, "ts-nkeys": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/ts-nkeys/-/ts-nkeys-1.0.12.tgz", - "integrity": "sha512-5TgA+wbfxTy/9pdSuAhvneuL65KKoI7phonzNQH2UhnorAQAWehUwHNLEuli596wu/Fxh0SAhMeKZVLNx4s7Ow==", + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/ts-nkeys/-/ts-nkeys-1.0.16.tgz", + "integrity": "sha512-1qrhAlavbm36wtW+7NtKOgxpzl+70NTF8xlz9mEhiA5zHMlMxjj3sEVKWm3pGZhHXE0Q3ykjrj+OSRVaYw+Dqg==", "requires": { - "tweetnacl": "^1.0.1" + "tweetnacl": "^1.0.3" } }, "tslib": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", "dev": true }, "tweetnacl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.1.tgz", - "integrity": "sha512-kcoMoKTPYnoeS50tzoqjPY3Uv9axeuuFAZY9M/9zFnhoVvRfxz9K29IMPD7jGmt2c8SW7i3gT9WqDl2+nV7p4A==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" }, "update-check": { "version": "1.5.2", @@ -1153,60 +1038,45 @@ "dev": true, "requires": { "string-width": "^2.1.1" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" }, "dependencies": { "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" } }, "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "^3.0.0" } } } }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } }, "y18n": { "version": "4.0.0", @@ -1221,29 +1091,27 @@ "dev": true }, "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", "dev": true, "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", + "cliui": "^5.0.0", "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", + "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", + "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", - "string-width": "^2.0.0", + "string-width": "^3.0.0", "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" } }, "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", "dev": true, "requires": { "camelcase": "^5.0.0", diff --git a/examples/edit-text/package.json b/examples/edit-text/package.json index 2367119..1d4315b 100644 --- a/examples/edit-text/package.json +++ b/examples/edit-text/package.json @@ -2,10 +2,10 @@ "name": "edit-text", "description": "Resgate Edit Text Example", "dependencies": { - "nats": "^1.3.0" + "nats": "^1.4.9" }, "devDependencies": { - "concurrently": "^4.1.1", + "concurrently": "^5.2.0", "serve": "^10.1.2" }, "scripts": { diff --git a/examples/hello-world/package-lock.json b/examples/hello-world/package-lock.json index a956e04..4aaf88d 100644 --- a/examples/hello-world/package-lock.json +++ b/examples/hello-world/package-lock.json @@ -10,13 +10,13 @@ "dev": true }, "accepts": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", - "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", "dev": true, "requires": { - "mime-types": "~2.1.18", - "negotiator": "0.6.1" + "mime-types": "~2.1.24", + "negotiator": "0.6.2" } }, "ajv": { @@ -38,12 +38,39 @@ "dev": true, "requires": { "string-width": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } } }, "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, "ansi-styles": { @@ -56,9 +83,9 @@ } }, "arch": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.1.1.tgz", - "integrity": "sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.1.2.tgz", + "integrity": "sha512-NTBIIbAfkJeIletyABbVtdPgeKfDafR+1mZV/AyyfC1UkVkp9iUjV+wwmqtUgphHYajbI86jejBJp5e+jkGTiQ==", "dev": true }, "arg": { @@ -88,11 +115,36 @@ "widest-line": "^2.0.0" }, "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, "camelcase": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } } } }, @@ -156,17 +208,6 @@ "execa": "^0.8.0" }, "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, "execa": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/execa/-/execa-0.8.0.tgz", @@ -181,32 +222,20 @@ "signal-exit": "^3.0.0", "strip-eof": "^1.0.0" } - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true } } }, "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", "dev": true, "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" } }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -223,12 +252,12 @@ "dev": true }, "compressible": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.16.tgz", - "integrity": "sha512-JQfEOdnI7dASwCuSPWIeVYwc/zMsu/+tRhoUvEfXz2gxOA2DNjmG5vhtFdBlhWPPGo+RdT9S3tgc/uH5qgDiiA==", + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", "dev": true, "requires": { - "mime-db": ">= 1.38.0 < 2" + "mime-db": ">= 1.43.0 < 2" } }, "compression": { @@ -253,20 +282,20 @@ "dev": true }, "concurrently": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-4.1.1.tgz", - "integrity": "sha512-48+FE5RJ0qc8azwKv4keVQWlni1hZeSjcWr8shBelOBtBHcKj1aJFM9lHRiSc1x7lq416pkvsqfBMhSRja+Lhw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-5.2.0.tgz", + "integrity": "sha512-XxcDbQ4/43d6CxR7+iV8IZXhur4KbmEJk1CetVMUqCy34z9l0DkszbY+/9wvmSnToTej0SYomc2WSRH+L0zVJw==", "dev": true, "requires": { - "chalk": "^2.4.1", - "date-fns": "^1.23.0", - "lodash": "^4.17.10", + "chalk": "^2.4.2", + "date-fns": "^2.0.1", + "lodash": "^4.17.15", "read-pkg": "^4.0.1", - "rxjs": "^6.3.3", + "rxjs": "^6.5.2", "spawn-command": "^0.0.2-1", - "supports-color": "^4.5.0", - "tree-kill": "^1.1.0", - "yargs": "^12.0.1" + "supports-color": "^6.1.0", + "tree-kill": "^1.2.2", + "yargs": "^13.3.0" } }, "content-disposition": { @@ -276,22 +305,20 @@ "dev": true }, "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", + "lru-cache": "^4.0.1", "shebang-command": "^1.2.0", "which": "^1.2.9" } }, "date-fns": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", - "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.14.0.tgz", + "integrity": "sha512-1zD+68jhFgDIM0rF05rcwYO8cExdNqxjq4xP1QKM60Q45mnO6zaMWB4tOzrIr4M4GSLntsKeE4c9Bdl2jhL/yw==", "dev": true }, "debug": { @@ -315,14 +342,11 @@ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true }, "error-ex": { "version": "1.3.2", @@ -340,13 +364,13 @@ "dev": true }, "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", "is-stream": "^1.1.0", "npm-run-path": "^2.0.0", "p-finally": "^1.0.0", @@ -361,9 +385,9 @@ "dev": true }, "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, "fast-url-parser": { @@ -393,19 +417,16 @@ } }, "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true }, "has-flag": { "version": "3.0.0", @@ -414,9 +435,9 @@ "dev": true }, "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", "dev": true }, "ini": { @@ -425,12 +446,6 @@ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -467,15 +482,6 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -502,47 +508,21 @@ "yallist": "^2.1.2" } }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, - "requires": { - "p-defer": "^1.0.0" - } - }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, "mime-db": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", - "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==", + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", "dev": true }, "mime-types": { - "version": "2.1.22", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", - "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", "dev": true, "requires": { - "mime-db": "~1.38.0" + "mime-db": "1.44.0" } }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -565,24 +545,18 @@ "dev": true }, "nats": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/nats/-/nats-1.3.0.tgz", - "integrity": "sha512-0Rbtl0WV5kXLcZlE5fbX06/YwVzvx9Ui5xaIsVH6rzRGu7FRUAHxueaZBPHIN3M82s3N4bbkSwghYipgcsIgbA==", + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/nats/-/nats-1.4.9.tgz", + "integrity": "sha512-D0qTg2v0jir32TCJfGkxRwOba4/9vqJJmr4GtghR/J9AF6E0y1BdCeXxnD2G+hruSPnKUbF0NzaQVPbSg5fdOg==", "requires": { - "nuid": "^1.0.0", - "ts-nkeys": "^1.0.8" + "nuid": "^1.1.4", + "ts-nkeys": "^1.0.16" } }, "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", "dev": true }, "normalize-package-data": { @@ -607,15 +581,9 @@ } }, "nuid": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/nuid/-/nuid-1.1.0.tgz", - "integrity": "sha512-C/JdZ6PtCqKsCEs4ni76nhBsdmuQgLAT/CTLNprkcLViDAnkk7qx5sSA8PVC2vmSsdBlSsFuGb52v6pwn1oaeg==" - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/nuid/-/nuid-1.1.4.tgz", + "integrity": "sha512-PXiYyHhGfrq8H4g5HyC8enO1lz6SBe5z6x1yx/JG4tmADzDGJVQy3l1sRf3VtEvPsN8dGn9hRFRwDKWL62x0BA==" }, "on-headers": { "version": "1.0.2", @@ -623,48 +591,16 @@ "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", "dev": true }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true - }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", "dev": true }, - "p-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", - "dev": true - }, "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -737,16 +673,6 @@ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -808,24 +734,24 @@ "dev": true }, "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, "resolve": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz", - "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", "dev": true, "requires": { "path-parse": "^1.0.6" } }, "rxjs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz", - "integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==", + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", + "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", "dev": true, "requires": { "tslib": "^1.9.0" @@ -838,9 +764,9 @@ "dev": true }, "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, "serve": { @@ -937,9 +863,9 @@ "dev": true }, "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, "spawn-command": { @@ -949,9 +875,9 @@ "dev": true }, "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", @@ -959,15 +885,15 @@ } }, "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", "dev": true }, "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, "requires": { "spdx-exceptions": "^2.1.0", @@ -981,22 +907,23 @@ "dev": true }, "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { + "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "strip-ansi": "^5.1.0" } }, "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "^4.1.0" } }, "strip-eof": { @@ -1012,20 +939,12 @@ "dev": true }, "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "requires": { - "has-flag": "^2.0.0" - }, - "dependencies": { - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - } + "has-flag": "^3.0.0" } }, "term-size": { @@ -1035,66 +954,32 @@ "dev": true, "requires": { "execa": "^0.7.0" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - } } }, "tree-kill": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.1.tgz", - "integrity": "sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true }, "ts-nkeys": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/ts-nkeys/-/ts-nkeys-1.0.12.tgz", - "integrity": "sha512-5TgA+wbfxTy/9pdSuAhvneuL65KKoI7phonzNQH2UhnorAQAWehUwHNLEuli596wu/Fxh0SAhMeKZVLNx4s7Ow==", + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/ts-nkeys/-/ts-nkeys-1.0.16.tgz", + "integrity": "sha512-1qrhAlavbm36wtW+7NtKOgxpzl+70NTF8xlz9mEhiA5zHMlMxjj3sEVKWm3pGZhHXE0Q3ykjrj+OSRVaYw+Dqg==", "requires": { - "tweetnacl": "^1.0.1" + "tweetnacl": "^1.0.3" } }, "tslib": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", "dev": true }, "tweetnacl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.1.tgz", - "integrity": "sha512-kcoMoKTPYnoeS50tzoqjPY3Uv9axeuuFAZY9M/9zFnhoVvRfxz9K29IMPD7jGmt2c8SW7i3gT9WqDl2+nV7p4A==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" }, "update-check": { "version": "1.5.2", @@ -1153,60 +1038,45 @@ "dev": true, "requires": { "string-width": "^2.1.1" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" }, "dependencies": { "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" } }, "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "^3.0.0" } } } }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } }, "y18n": { "version": "4.0.0", @@ -1221,29 +1091,27 @@ "dev": true }, "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", "dev": true, "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", + "cliui": "^5.0.0", "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", + "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", + "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", - "string-width": "^2.0.0", + "string-width": "^3.0.0", "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" } }, "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", "dev": true, "requires": { "camelcase": "^5.0.0", diff --git a/examples/hello-world/package.json b/examples/hello-world/package.json index 33c740c..1c231d0 100644 --- a/examples/hello-world/package.json +++ b/examples/hello-world/package.json @@ -2,10 +2,10 @@ "name": "hello-world", "description": "Resgate Hello World Example", "dependencies": { - "nats": "^1.3.0" + "nats": "^1.4.9" }, "devDependencies": { - "concurrently": "^4.1.1", + "concurrently": "^5.2.0", "serve": "^10.1.2" }, "scripts": { diff --git a/examples/jwt-authentication/package-lock.json b/examples/jwt-authentication/package-lock.json index 49cf824..4ee31d2 100644 --- a/examples/jwt-authentication/package-lock.json +++ b/examples/jwt-authentication/package-lock.json @@ -207,9 +207,9 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ipaddr.js": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", - "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, "jsonwebtoken": { "version": "8.5.1", @@ -310,16 +310,16 @@ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" }, "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", "requires": { - "mime-db": "1.40.0" + "mime-db": "1.44.0" } }, "ms": { @@ -328,12 +328,12 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "nats": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/nats/-/nats-1.3.0.tgz", - "integrity": "sha512-0Rbtl0WV5kXLcZlE5fbX06/YwVzvx9Ui5xaIsVH6rzRGu7FRUAHxueaZBPHIN3M82s3N4bbkSwghYipgcsIgbA==", + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/nats/-/nats-1.4.9.tgz", + "integrity": "sha512-D0qTg2v0jir32TCJfGkxRwOba4/9vqJJmr4GtghR/J9AF6E0y1BdCeXxnD2G+hruSPnKUbF0NzaQVPbSg5fdOg==", "requires": { - "nuid": "^1.0.0", - "ts-nkeys": "^1.0.8" + "nuid": "^1.1.4", + "ts-nkeys": "^1.0.16" } }, "negotiator": { @@ -342,9 +342,9 @@ "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, "nuid": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/nuid/-/nuid-1.1.0.tgz", - "integrity": "sha512-C/JdZ6PtCqKsCEs4ni76nhBsdmuQgLAT/CTLNprkcLViDAnkk7qx5sSA8PVC2vmSsdBlSsFuGb52v6pwn1oaeg==" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/nuid/-/nuid-1.1.4.tgz", + "integrity": "sha512-PXiYyHhGfrq8H4g5HyC8enO1lz6SBe5z6x1yx/JG4tmADzDGJVQy3l1sRf3VtEvPsN8dGn9hRFRwDKWL62x0BA==" }, "on-finished": { "version": "2.3.0", @@ -365,12 +365,12 @@ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, "proxy-addr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", - "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", "requires": { "forwarded": "~0.1.2", - "ipaddr.js": "1.9.0" + "ipaddr.js": "1.9.1" } }, "qs": { @@ -405,9 +405,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, "send": { "version": "0.17.1", @@ -463,17 +463,17 @@ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, "ts-nkeys": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/ts-nkeys/-/ts-nkeys-1.0.12.tgz", - "integrity": "sha512-5TgA+wbfxTy/9pdSuAhvneuL65KKoI7phonzNQH2UhnorAQAWehUwHNLEuli596wu/Fxh0SAhMeKZVLNx4s7Ow==", + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/ts-nkeys/-/ts-nkeys-1.0.16.tgz", + "integrity": "sha512-1qrhAlavbm36wtW+7NtKOgxpzl+70NTF8xlz9mEhiA5zHMlMxjj3sEVKWm3pGZhHXE0Q3ykjrj+OSRVaYw+Dqg==", "requires": { - "tweetnacl": "^1.0.1" + "tweetnacl": "^1.0.3" } }, "tweetnacl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.1.tgz", - "integrity": "sha512-kcoMoKTPYnoeS50tzoqjPY3Uv9axeuuFAZY9M/9zFnhoVvRfxz9K29IMPD7jGmt2c8SW7i3gT9WqDl2+nV7p4A==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" }, "type-is": { "version": "1.6.18", diff --git a/examples/jwt-authentication/package.json b/examples/jwt-authentication/package.json index bccceda..6ae1f1b 100644 --- a/examples/jwt-authentication/package.json +++ b/examples/jwt-authentication/package.json @@ -5,7 +5,7 @@ "cookie": "^0.3.1", "express": "^4.17.1", "jsonwebtoken": "^8.5.1", - "nats": "^1.3.0" + "nats": "^1.4.9" }, "scripts": { "start": "node server.js" diff --git a/examples/password-authentication/package-lock.json b/examples/password-authentication/package-lock.json index 7768233..b07a4c5 100644 --- a/examples/password-authentication/package-lock.json +++ b/examples/password-authentication/package-lock.json @@ -187,9 +187,9 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ipaddr.js": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", - "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, "media-typer": { "version": "0.3.0", @@ -212,16 +212,16 @@ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" }, "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", "requires": { - "mime-db": "1.40.0" + "mime-db": "1.44.0" } }, "ms": { @@ -230,12 +230,12 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "nats": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/nats/-/nats-1.3.0.tgz", - "integrity": "sha512-0Rbtl0WV5kXLcZlE5fbX06/YwVzvx9Ui5xaIsVH6rzRGu7FRUAHxueaZBPHIN3M82s3N4bbkSwghYipgcsIgbA==", + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/nats/-/nats-1.4.9.tgz", + "integrity": "sha512-D0qTg2v0jir32TCJfGkxRwOba4/9vqJJmr4GtghR/J9AF6E0y1BdCeXxnD2G+hruSPnKUbF0NzaQVPbSg5fdOg==", "requires": { - "nuid": "^1.0.0", - "ts-nkeys": "^1.0.8" + "nuid": "^1.1.4", + "ts-nkeys": "^1.0.16" } }, "negotiator": { @@ -244,9 +244,9 @@ "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, "nuid": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/nuid/-/nuid-1.1.0.tgz", - "integrity": "sha512-C/JdZ6PtCqKsCEs4ni76nhBsdmuQgLAT/CTLNprkcLViDAnkk7qx5sSA8PVC2vmSsdBlSsFuGb52v6pwn1oaeg==" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/nuid/-/nuid-1.1.4.tgz", + "integrity": "sha512-PXiYyHhGfrq8H4g5HyC8enO1lz6SBe5z6x1yx/JG4tmADzDGJVQy3l1sRf3VtEvPsN8dGn9hRFRwDKWL62x0BA==" }, "on-finished": { "version": "2.3.0", @@ -267,12 +267,12 @@ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, "proxy-addr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", - "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", "requires": { "forwarded": "~0.1.2", - "ipaddr.js": "1.9.0" + "ipaddr.js": "1.9.1" } }, "qs": { @@ -360,17 +360,17 @@ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, "ts-nkeys": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/ts-nkeys/-/ts-nkeys-1.0.12.tgz", - "integrity": "sha512-5TgA+wbfxTy/9pdSuAhvneuL65KKoI7phonzNQH2UhnorAQAWehUwHNLEuli596wu/Fxh0SAhMeKZVLNx4s7Ow==", + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/ts-nkeys/-/ts-nkeys-1.0.16.tgz", + "integrity": "sha512-1qrhAlavbm36wtW+7NtKOgxpzl+70NTF8xlz9mEhiA5zHMlMxjj3sEVKWm3pGZhHXE0Q3ykjrj+OSRVaYw+Dqg==", "requires": { - "tweetnacl": "^1.0.1" + "tweetnacl": "^1.0.3" } }, "tweetnacl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.1.tgz", - "integrity": "sha512-kcoMoKTPYnoeS50tzoqjPY3Uv9axeuuFAZY9M/9zFnhoVvRfxz9K29IMPD7jGmt2c8SW7i3gT9WqDl2+nV7p4A==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" }, "type-is": { "version": "1.6.18", diff --git a/examples/password-authentication/package.json b/examples/password-authentication/package.json index 2930d8c..2d15812 100644 --- a/examples/password-authentication/package.json +++ b/examples/password-authentication/package.json @@ -3,7 +3,7 @@ "description": "Resgate Password Authentication Example", "dependencies": { "express": "^4.17.1", - "nats": "^1.3.0" + "nats": "^1.4.9" }, "scripts": { "start": "node server.js" diff --git a/go.mod b/go.mod index 1c55909..9b909e9 100644 --- a/go.mod +++ b/go.mod @@ -5,9 +5,7 @@ go 1.13 require ( github.com/gorilla/websocket v1.4.2 github.com/jirenius/timerqueue v1.0.0 - github.com/nats-io/nats-server/v2 v2.1.4 // indirect - github.com/nats-io/nats.go v1.9.1 + github.com/nats-io/nats.go v1.10.0 github.com/posener/wstest v1.2.0 github.com/rs/xid v1.2.1 - golang.org/x/crypto v0.0.0-20200317142112-1b76d66859c6 // indirect ) diff --git a/go.sum b/go.sum index 733a3f4..a99c097 100644 --- a/go.sum +++ b/go.sum @@ -1,47 +1,32 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/jirenius/timerqueue v1.0.0 h1:TgcUQlrxKBBHYmStXPzLdMPJFfmqkWZZ1s7BA5G1d9E= github.com/jirenius/timerqueue v1.0.0/go.mod h1:pUEjy16BUruJMjLIsjWvWQh9Bu9CSXCIfGADZf37WIk= -github.com/nats-io/jwt v0.3.0 h1:xdnzwFETV++jNc4W1mw//qFyJGb2ABOombmZJQS4+Qo= -github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2 h1:+RB5hMpXUUA2dfxuhBTEkMOrYmM+gKIZYS1KjSostMI= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/nats-server/v2 v2.1.4 h1:BILRnsJ2Yb/fefiFbBWADpViGF69uh4sxe8poVDQ06g= -github.com/nats-io/nats-server/v2 v2.1.4/go.mod h1:Jw1Z28soD/QasIA2uWjXyM9El1jly3YwyFOuR8tH1rg= -github.com/nats-io/nats.go v1.9.1 h1:ik3HbLhZ0YABLto7iX80pZLPw/6dx3T+++MZJwLnMrQ= -github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nkeys v0.1.0 h1:qMd4+pRHgdr1nAClu+2h/2a5F2TmKcCzjCDazVgRoX4= -github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.3 h1:6JrEfig+HzTH85yxzhSVbjHRJv9cn0p6n3IngIcM5/k= +github.com/nats-io/nats.go v1.10.0 h1:L8qnKaofSfNFbXg0C5F71LdjPRnmQwSsA4ukmkt1TvY= +github.com/nats-io/nats.go v1.10.0/go.mod h1:AjGArbfyR50+afOUotNX2Xs5SYHf+CoOa5HH1eEl2HE= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.4 h1:aEsHIssIk6ETN5m2/MD8Y4B2X7FfXrBAUdkyRvbVYzA= +github.com/nats-io/nkeys v0.1.4/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/wstest v1.2.0 h1:PAY0cRybxOjh0yqSDCrlAGUwtx+GNKpuUfid/08pv48= github.com/posener/wstest v1.2.0/go.mod h1:GkplCx9zskpudjrMp23LyZHrSonab0aZzh2x0ACGRbU= github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200317142112-1b76d66859c6 h1:TjszyFsQsyZNHwdVdZ5m7bjmreu0znc2kRYsEml9/Ww= -golang.org/x/crypto v0.0.0-20200317142112-1b76d66859c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM= +golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e h1:D5TXcfTk7xF7hvieo4QErS3qqCB4teTffacDWr7CI+0= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/main.go b/main.go index 2a0ee63..e8f9264 100644 --- a/main.go +++ b/main.go @@ -249,7 +249,7 @@ func usage() { // version will print out the current resgate and protocol version. func version() { - fmt.Printf("resgate v%s\nprotocol v%s", server.Version, server.ProtocolVersion) + fmt.Printf("resgate v%s\nprotocol v%s\n", server.Version, server.ProtocolVersion) os.Exit(0) } diff --git a/nats/nats.go b/nats/nats.go index ee35ffc..2f16b6f 100644 --- a/nats/nats.go +++ b/nats/nats.go @@ -215,7 +215,7 @@ func (c *Client) listener(ch chan *nats.Msg, stopped chan struct{}) { if ok && rc.isReq { // Is the first character a-z or A-Z? // Then it is a meta response - if len(msg.Data) > 0 && (msg.Data[0]|32)-'a' < 26 { + if len(msg.Data) > 0 && (msg.Data[0]|32) >= 'a' && (msg.Data[0]|32) <= 'z' { c.parseMeta(msg, rc) c.mu.Unlock() c.Tracef("==> (%s): %s", inboxSubstr(msg.Subject), msg.Data) diff --git a/server/apiEncoding.go b/server/apiEncoding.go index a36eeed..f250afd 100644 --- a/server/apiEncoding.go +++ b/server/apiEncoding.go @@ -214,13 +214,8 @@ func (e *encoderJSON) encodeSubscription(s *Subscription, wrap bool) error { if i > 0 { e.b.WriteByte(',') } - if v.Type == codec.ValueTypeResource { - sc := s.Ref(v.RID) - if err := e.encodeSubscription(sc, true); err != nil { - return err - } - } else { - e.b.Write(v.RawMessage) + if err := e.encodeValue(s, v); err != nil { + return err } } e.b.WriteByte(']') @@ -247,13 +242,8 @@ func (e *encoderJSON) encodeSubscription(s *Subscription, wrap bool) error { e.b.Write(dta) e.b.WriteByte(':') - if v.Type == codec.ValueTypeResource { - sc := s.Ref(v.RID) - if err := e.encodeSubscription(sc, true); err != nil { - return err - } - } else { - e.b.Write(v.RawMessage) + if err := e.encodeValue(s, v); err != nil { + return err } } e.b.WriteByte('}') @@ -265,6 +255,29 @@ func (e *encoderJSON) encodeSubscription(s *Subscription, wrap bool) error { return nil } +func (e *encoderJSON) encodeValue(s *Subscription, v codec.Value) error { + switch v.Type { + case codec.ValueTypeReference: + sc := s.Ref(v.RID) + if err := e.encodeSubscription(sc, true); err != nil { + return err + } + case codec.ValueTypeSoftReference: + e.b.Write([]byte(`{"href":`)) + dta, err := json.Marshal(RIDToPath(v.RID, e.apiPath)) + if err != nil { + return err + } + e.b.Write(dta) + e.b.WriteByte('}') + case codec.ValueTypeData: + e.b.Write(v.Inner) + default: + e.b.Write(v.RawMessage) + } + return nil +} + type encoderJSONFlat struct { b bytes.Buffer path []string @@ -338,13 +351,8 @@ func (e *encoderJSONFlat) encodeSubscription(s *Subscription) error { if i > 0 { e.b.WriteByte(',') } - if v.Type == codec.ValueTypeResource { - sc := s.Ref(v.RID) - if err := e.encodeSubscription(sc); err != nil { - return err - } - } else { - e.b.Write(v.RawMessage) + if err := e.encodeValue(s, v); err != nil { + return err } } e.b.WriteByte(']') @@ -368,13 +376,8 @@ func (e *encoderJSONFlat) encodeSubscription(s *Subscription) error { e.b.Write(dta) e.b.WriteByte(':') - if v.Type == codec.ValueTypeResource { - sc := s.Ref(v.RID) - if err := e.encodeSubscription(sc); err != nil { - return err - } - } else { - e.b.Write(v.RawMessage) + if err := e.encodeValue(s, v); err != nil { + return err } } e.b.WriteByte('}') @@ -385,6 +388,29 @@ func (e *encoderJSONFlat) encodeSubscription(s *Subscription) error { return nil } +func (e *encoderJSONFlat) encodeValue(s *Subscription, v codec.Value) error { + switch v.Type { + case codec.ValueTypeReference: + sc := s.Ref(v.RID) + if err := e.encodeSubscription(sc); err != nil { + return err + } + case codec.ValueTypeSoftReference: + e.b.Write([]byte(`{"href":`)) + dta, err := json.Marshal(RIDToPath(v.RID, e.apiPath)) + if err != nil { + return err + } + e.b.Write(dta) + e.b.WriteByte('}') + case codec.ValueTypeData: + e.b.Write(v.Inner) + default: + e.b.Write(v.RawMessage) + } + return nil +} + func jsonEncodeError(rerr *reserr.Error) []byte { out, err := json.Marshal(rerr) if err != nil { diff --git a/server/apiHandler.go b/server/apiHandler.go index 7dab2a8..d23c5a6 100644 --- a/server/apiHandler.go +++ b/server/apiHandler.go @@ -173,7 +173,7 @@ func (s *Service) handleCall(w http.ResponseWriter, r *http.Request, rid string, } func (s *Service) temporaryConn(w http.ResponseWriter, r *http.Request, cb func(*wsConn, func([]byte, error))) { - c := s.newWSConn(nil, r, latestProtocol) + c := s.newWSConn(nil, r, versionLatest) if c == nil { httpError(w, reserr.ErrServiceUnavailable, s.enc) return diff --git a/server/codec/codec.go b/server/codec/codec.go index f44959d..c1746a6 100644 --- a/server/codec/codec.go +++ b/server/codec/codec.go @@ -175,23 +175,34 @@ type ValueType byte // Value type constants const ( ValueTypeNone ValueType = iota - ValueTypePrimitive - ValueTypeResource ValueTypeDelete + ValueTypePrimitive + ValueTypeReference + ValueTypeSoftReference + ValueTypeData ) // Value represents a RES value // https://github.com/resgateio/resgate/blob/master/docs/res-protocol.md#values type Value struct { json.RawMessage - Type ValueType - RID string + Type ValueType + RID string + Inner json.RawMessage } // ValueObject represents a resource reference or an action type ValueObject struct { - RID *string `json:"rid"` - Action *string `json:"action"` + RID *string `json:"rid"` + Soft bool `json:"soft"` + Action *string `json:"action"` + Data json.RawMessage `json:"data"` +} + +// IsProper returns true if the value's type is either a primitive, a +// reference, or a data value. +func (v Value) IsProper() bool { + return v.Type >= ValueTypePrimitive } // DeleteValue is a predeclared delete action value @@ -226,22 +237,39 @@ func (v *Value) UnmarshalJSON(data []byte) error { return err } - if mvo.RID != nil { - // Invalid to have both RID and Action set, or if RID is empty - if mvo.Action != nil || *mvo.RID == "" { + switch { + case mvo.RID != nil: + // Invalid to have both RID and Action or Data set, or if RID is empty + if mvo.Action != nil || mvo.Data != nil || *mvo.RID == "" { return errInvalidValue } - v.Type = ValueTypeResource v.RID = *mvo.RID if !IsValidRID(v.RID, true) { return errInvalidValue } - } else { - // Must be an action of type actionDelete - if mvo.Action == nil || *mvo.Action != actionDelete { + if mvo.Soft { + v.Type = ValueTypeSoftReference + } else { + v.Type = ValueTypeReference + } + case mvo.Action != nil: + // Invalid to have both Action and Data set, or if action is not actionDelete + if mvo.Data != nil || *mvo.Action != actionDelete { return errInvalidValue } v.Type = ValueTypeDelete + case mvo.Data != nil: + v.Inner = mvo.Data + dc := mvo.Data[0] + // Is data containing a primitive? + if dc == '{' || dc == '[' { + v.Type = ValueTypeData + } else { + v.RawMessage = mvo.Data + v.Type = ValueTypePrimitive + } + default: + return errInvalidValue } case '[': return errInvalidValue @@ -259,9 +287,13 @@ func (v Value) Equal(w Value) bool { } switch v.Type { + case ValueTypeData: + fallthrough case ValueTypePrimitive: return bytes.Equal(v.RawMessage, w.RawMessage) - case ValueTypeResource: + case ValueTypeReference: + fallthrough + case ValueTypeSoftReference: return v.RID == w.RID } @@ -320,14 +352,14 @@ func DecodeGetResponse(payload []byte) (*GetResult, error) { } // Assert model only has proper values for _, v := range res.Model { - if v.Type != ValueTypeResource && v.Type != ValueTypePrimitive { + if !v.IsProper() { return nil, errInvalidResponse } } } else if res.Collection != nil { // Assert collection only has proper values for _, v := range res.Collection { - if v.Type != ValueTypeResource && v.Type != ValueTypePrimitive { + if !v.IsProper() { return nil, errInvalidResponse } } @@ -397,14 +429,14 @@ func DecodeEventQueryResponse(payload []byte) (*EventQueryResult, error) { } // Assert model only has proper values for _, v := range res.Model { - if v.Type != ValueTypeResource && v.Type != ValueTypePrimitive { + if !v.IsProper() { return nil, errInvalidResponse } } case res.Collection != nil: // Assert collection only has proper values for _, v := range res.Collection { - if v.Type != ValueTypeResource && v.Type != ValueTypePrimitive { + if !v.IsProper() { return nil, errInvalidResponse } } @@ -483,8 +515,7 @@ func DecodeAddEvent(data json.RawMessage) (*AddEvent, error) { } // Assert it is a proper value - t := d.Value.Type - if t != ValueTypeResource && t != ValueTypePrimitive { + if !d.Value.IsProper() { return nil, errInvalidValue } diff --git a/server/const.go b/server/const.go index 1eadcb2..5ae7ca5 100644 --- a/server/const.go +++ b/server/const.go @@ -4,10 +4,10 @@ import "time" const ( // Version is the current version for the server. - Version = "1.5.0" + Version = "1.6.0" // ProtocolVersion is the implemented RES protocol version. - ProtocolVersion = "1.2.0" + ProtocolVersion = "1.2.1" // DefaultAddr is the default host for client connections. DefaultAddr = "0.0.0.0" diff --git a/server/rescache/legacy.go b/server/rescache/legacy.go new file mode 100644 index 0000000..f2a7edf --- /dev/null +++ b/server/rescache/legacy.go @@ -0,0 +1,93 @@ +package rescache + +import ( + "bytes" + "encoding/json" + + "github.com/resgateio/resgate/server/codec" +) + +// Legacy120Model marshals a model compatible with version 1.2.0 +// (versionSoftResourceReferenceAndDataValue) and below. +type Legacy120Model Model + +// Legacy120Collection marshals a collection compatible with version 1.2.0 +// (versionSoftResourceReferenceAndDataValue) and below. +type Legacy120Collection Collection + +// Legacy120Value marshals a value compatible with version 1.2.0 +// (versionSoftResourceReferenceAndDataValue) and below. +type Legacy120Value codec.Value + +// Legacy120ValueMap marshals a map of values compatible with version 1.2.0 +// (versionSoftResourceReferenceAndDataValue) and below. +type Legacy120ValueMap map[string]codec.Value + +var legacyDataPlaceholderBytes = []byte(`"[Data]"`) + +// MarshalJSON creates a JSON encoded representation of the model +func (m *Legacy120Model) MarshalJSON() ([]byte, error) { + for _, v := range m.Values { + if v.Type == codec.ValueTypeSoftReference || v.Type == codec.ValueTypeData { + return Legacy120ValueMap(m.Values).MarshalJSON() + } + } + return (*Model)(m).MarshalJSON() +} + +// MarshalJSON creates a JSON encoded representation of the model +func (c *Legacy120Collection) MarshalJSON() ([]byte, error) { + for _, v := range c.Values { + if v.Type == codec.ValueTypeSoftReference || v.Type == codec.ValueTypeData { + goto LegacyMarshal + } + } + return (*Collection)(c).MarshalJSON() + +LegacyMarshal: + + vs := c.Values + lvs := make([]Legacy120Value, len(vs)) + for i, v := range vs { + lvs[i] = Legacy120Value(v) + } + + return json.Marshal(lvs) +} + +// MarshalJSON creates a JSON encoded representation of the value +func (v Legacy120Value) MarshalJSON() ([]byte, error) { + if v.Type == codec.ValueTypeSoftReference { + return json.Marshal(v.RID) + } + if v.Type == codec.ValueTypeData { + return legacyDataPlaceholderBytes, nil + } + return v.RawMessage, nil +} + +// MarshalJSON creates a JSON encoded representation of the map +func (m Legacy120ValueMap) MarshalJSON() ([]byte, error) { + var b bytes.Buffer + notfirst := false + b.WriteByte('{') + for k, v := range m { + if notfirst { + b.WriteByte(',') + } + notfirst = true + dta, err := json.Marshal(k) + if err != nil { + return nil, err + } + b.Write(dta) + b.WriteByte(':') + dta, err = Legacy120Value(v).MarshalJSON() + if err != nil { + return nil, err + } + b.Write(dta) + } + b.WriteByte('}') + return b.Bytes(), nil +} diff --git a/server/rescache/legacy_test.go b/server/rescache/legacy_test.go new file mode 100644 index 0000000..36c3079 --- /dev/null +++ b/server/rescache/legacy_test.go @@ -0,0 +1,142 @@ +package rescache_test + +import ( + "encoding/json" + "fmt" + "reflect" + "testing" + + "github.com/resgateio/resgate/server/codec" + "github.com/resgateio/resgate/server/rescache" +) + +func TestLegacy120Model_MarshalJSON_ReturnsSoftReferenceAsString(t *testing.T) { + var v map[string]codec.Value + dta := []byte(`{"name":"softparent","child":{"rid":"test.model","soft":true}}`) + expected := []byte(`{"name":"softparent","child":"test.model"}`) + err := json.Unmarshal(dta, &v) + if err != nil { + t.Fatal(err) + } + m := &rescache.Model{Values: v} + lm := (*rescache.Legacy120Model)(m) + + out, err := lm.MarshalJSON() + if err != nil { + t.Fatal(err) + } + + AssertEqualJSON(t, "Legacy120Model.MarshalJSON", json.RawMessage(out), json.RawMessage(expected)) +} + +func TestLegacy120Model_MarshalJSON_ReturnsDataValuePlaceholder(t *testing.T) { + var v map[string]codec.Value + dta := []byte(`{"name":"data","primitive":{"data":12},"object":{"data":{"foo":["bar"]}},"array":{"data":[{"foo":"bar"}]}}`) + expected := []byte(`{"name":"data","primitive":12,"object":"[Data]","array":"[Data]"}`) + err := json.Unmarshal(dta, &v) + if err != nil { + t.Fatal(err) + } + m := &rescache.Model{Values: v} + lm := (*rescache.Legacy120Model)(m) + + out, err := lm.MarshalJSON() + if err != nil { + t.Fatal(err) + } + + AssertEqualJSON(t, "Legacy120Model.MarshalJSON", json.RawMessage(out), json.RawMessage(expected)) +} + +func TestLegacy120Collection_MarshalJSON_ReturnsSoftReferenceAsString(t *testing.T) { + var v []codec.Value + dta := []byte(`["softparent",{"rid":"test.model","soft":true}]`) + expected := []byte(`["softparent","test.model"]`) + err := json.Unmarshal(dta, &v) + if err != nil { + t.Fatal(err) + } + m := &rescache.Collection{Values: v} + lm := (*rescache.Legacy120Collection)(m) + + out, err := lm.MarshalJSON() + if err != nil { + t.Fatal(err) + } + + AssertEqualJSON(t, "Legacy120Collection.MarshalJSON", json.RawMessage(out), json.RawMessage(expected)) +} + +func TestLegacy120Collection_MarshalJSON_ReturnsDataValuePlaceholder(t *testing.T) { + var v []codec.Value + dta := []byte(`["data",{"data":12},{"data":{"foo":["bar"]}},{"data":[{"foo":"bar"}]}]`) + expected := []byte(`["data",12,"[Data]","[Data]"]`) + err := json.Unmarshal(dta, &v) + if err != nil { + t.Fatal(err) + } + m := &rescache.Collection{Values: v} + lm := (*rescache.Legacy120Collection)(m) + + out, err := lm.MarshalJSON() + if err != nil { + t.Fatal(err) + } + + AssertEqualJSON(t, "Legacy120Collection.MarshalJSON", json.RawMessage(out), json.RawMessage(expected)) +} + +func TestLegacy120Value_MarshalJSON_ReturnsSoftReferenceAsString(t *testing.T) { + var v codec.Value + dta := []byte(`{"rid":"test.model","soft":true}`) + expected := []byte(`"test.model"`) + err := json.Unmarshal(dta, &v) + if err != nil { + t.Fatal(err) + } + lv := rescache.Legacy120Value(v) + + out, err := lv.MarshalJSON() + if err != nil { + t.Fatal(err) + } + + AssertEqualJSON(t, "Legacy120Value.MarshalJSON", json.RawMessage(out), json.RawMessage(expected)) +} + +// AssertEqualJSON expects that a and b json marshals into equal values, and +// returns true if they do, otherwise logs a fatal error and returns false. +func AssertEqualJSON(t *testing.T, name string, result, expected interface{}, ctx ...interface{}) bool { + aa, aj := jsonMap(t, result) + bb, bj := jsonMap(t, expected) + + if !reflect.DeepEqual(aa, bb) { + t.Fatalf("expected %s to be:\n\t%s\nbut got:\n\t%s%s", name, bj, aj, ctxString(ctx)) + return false + } + + return true +} + +func ctxString(ctx []interface{}) string { + if len(ctx) == 0 { + return "" + } + return "\nin " + fmt.Sprint(ctx...) +} + +func jsonMap(t *testing.T, v interface{}) (interface{}, []byte) { + var err error + j, err := json.Marshal(v) + if err != nil { + panic(fmt.Sprintf("test: error marshaling value:\n\t%+v\nerror:\n\t%s", v, err)) + } + + var m interface{} + err = json.Unmarshal(j, &m) + if err != nil { + panic(fmt.Sprintf("test: error unmarshaling value:\n\t%s\nerror:\n\t%s", j, err)) + } + + return m, j +} diff --git a/server/rescache/resourceSubscription.go b/server/rescache/resourceSubscription.go index 4e032f0..af089c4 100644 --- a/server/rescache/resourceSubscription.go +++ b/server/rescache/resourceSubscription.go @@ -2,6 +2,7 @@ package rescache import ( "encoding/json" + "errors" "github.com/resgateio/resgate/server/codec" "github.com/resgateio/resgate/server/reserr" @@ -453,6 +454,9 @@ func (rs *ResourceSubscription) processResetGetResponse(payload []byte, err erro // or an error in the service's response if err == nil { result, err = codec.DecodeGetResponse(payload) + if err == nil && ((rs.state == stateModel && result.Model == nil) || (rs.state == stateCollection && result.Collection == nil)) { + err = errors.New("mismatching resource type") + } } // Get request failed diff --git a/server/rpc/rpc.go b/server/rpc/rpc.go index 496d7c7..b7eac67 100644 --- a/server/rpc/rpc.go +++ b/server/rpc/rpc.go @@ -15,7 +15,7 @@ type Requester interface { Reply(data []byte) GetResource(rid string, callback func(data *Resources, err error)) SubscribeResource(rid string, callback func(data *Resources, err error)) - UnsubscribeResource(rid string, callback func(ok bool)) + UnsubscribeResource(rid string, count int, callback func(ok bool)) CallResource(rid, action string, params interface{}, callback func(result interface{}, err error)) AuthResource(rid, action string, params interface{}, callback func(result interface{}, err error)) NewResource(rid string, params interface{}, callback func(result interface{}, err error)) @@ -99,6 +99,11 @@ type CallResourceResult struct { *Resources } +// UnsubscribeRequest represents the params of an unsubscribe request +type UnsubscribeRequest struct { + Count *int `json:"count"` +} + var ( errMissingID = errors.New("Request is missing id property") ) @@ -121,7 +126,7 @@ func HandleRequest(data []byte, req Requester) error { if idx < 0 { if r.Method == "version" { var vr VersionRequest - if data != nil && !bytes.Equal(r.Params, nullBytes) { + if len(r.Params) > 0 && !bytes.Equal(r.Params, nullBytes) { err := json.Unmarshal(r.Params, &vr) if err != nil { req.Reply(r.ErrorResponse(reserr.ErrInvalidParams)) @@ -181,7 +186,23 @@ func HandleRequest(data []byte, req Requester) error { } }) case "unsubscribe": - req.UnsubscribeResource(rid, func(ok bool) { + count := 1 + if len(r.Params) > 0 && !bytes.Equal(r.Params, nullBytes) { + var ur UnsubscribeRequest + err := json.Unmarshal(r.Params, &ur) + if err != nil { + req.Reply(r.ErrorResponse(reserr.ErrInvalidParams)) + return nil + } + if ur.Count != nil { + count = *ur.Count + if count <= 0 { + req.Reply(r.ErrorResponse(reserr.ErrInvalidParams)) + return nil + } + } + } + req.UnsubscribeResource(rid, count, func(ok bool) { if ok { req.Reply(r.SuccessResponse(nil)) } else { diff --git a/server/subscription.go b/server/subscription.go index c959011..2306b66 100644 --- a/server/subscription.go +++ b/server/subscription.go @@ -27,6 +27,7 @@ type ConnSubscriber interface { Enqueue(f func()) bool ExpandCID(string) string Disconnect(reason string) + ProtocolVersion() int } // Subscription represents a resource subscription made by a client connection @@ -265,7 +266,11 @@ func (s *Subscription) onLoaded(rcb *readyCallback) { // It will lock the subscription and queue any events until ReleaseRPCResources is called. func (s *Subscription) GetRPCResources() *rpc.Resources { r := &rpc.Resources{} - s.populateResources(r) + if s.c.ProtocolVersion() < versionSoftResourceReferenceAndDataValue { + s.populateResourcesLegacy(r) + } else { + s.populateResources(r) + } return r } @@ -358,6 +363,48 @@ func (s *Subscription) populateResources(r *rpc.Resources) { } } +// populateResourcesLegacy is the same as populateResources, but uses legacy +// encodings of resources. +func (s *Subscription) populateResourcesLegacy(r *rpc.Resources) { + // Quick exit if resource is already sent + if s.state == stateSent || s.state == stateToSend { + return + } + + // Check for errors + err := s.Error() + if err != nil { + // Create Errors map if needed + if r.Errors == nil { + r.Errors = make(map[string]*reserr.Error) + } + r.Errors[s.rid] = reserr.RESError(err) + return + } + + switch s.typ { + case rescache.TypeCollection: + // Create Collections map if needed + if r.Collections == nil { + r.Collections = make(map[string]interface{}) + } + r.Collections[s.rid] = (*rescache.Legacy120Collection)(s.collection) + + case rescache.TypeModel: + // Create Models map if needed + if r.Models == nil { + r.Models = make(map[string]interface{}) + } + r.Models[s.rid] = (*rescache.Legacy120Model)(s.model) + } + + s.state = stateToSend + + for _, sc := range s.refs { + sc.sub.populateResourcesLegacy(r) + } +} + // setModel subscribes to all resource references in the model. func (s *Subscription) setModel() { m := s.resourceSub.GetModel() @@ -390,7 +437,7 @@ func (s *Subscription) setCollection() { // be unsubscribed, s.err set, s.doneLoading called, and false returned. // If v is not a resource reference, nothing will happen. func (s *Subscription) subscribeRef(v codec.Value) bool { - if v.Type != codec.ValueTypeResource { + if v.Type != codec.ValueTypeReference { return true } @@ -527,7 +574,7 @@ func (s *Subscription) processCollectionEvent(event *rescache.ResourceEvent) { idx := event.Idx switch v.Type { - case codec.ValueTypeResource: + case codec.ValueTypeReference: rid := v.RID sub, err := s.addReference(rid) if err != nil { @@ -558,6 +605,14 @@ func (s *Subscription) processCollectionEvent(event *rescache.ResourceEvent) { s.unqueueEvents(queueReasonLoading) }) + case codec.ValueTypeData: + fallthrough + case codec.ValueTypeSoftReference: + if s.c.ProtocolVersion() < versionSoftResourceReferenceAndDataValue { + s.c.Send(rpc.NewEvent(s.rid, event.Event, rpc.AddEvent{Idx: idx, Value: rescache.Legacy120Value(v)})) + break + } + fallthrough case codec.ValueTypePrimitive: s.c.Send(rpc.NewEvent(s.rid, event.Event, rpc.AddEvent{Idx: idx, Value: v.RawMessage})) } @@ -566,7 +621,7 @@ func (s *Subscription) processCollectionEvent(event *rescache.ResourceEvent) { // Remove and unsubscribe to model v := event.Value - if v.Type == codec.ValueTypeResource { + if v.Type == codec.ValueTypeReference { s.removeReference(v.RID) } s.c.Send(rpc.NewEvent(s.rid, event.Event, event.Payload)) @@ -587,7 +642,7 @@ func (s *Subscription) processModelEvent(event *rescache.ResourceEvent) { var subs []*Subscription for _, v := range ch { - if v.Type == codec.ValueTypeResource { + if v.Type == codec.ValueTypeReference { sub, err := s.addReference(v.RID) if err != nil { s.c.Errorf("Subscription %s: Error subscribing to resource %s: %s", s.rid, v.RID, err) @@ -606,14 +661,19 @@ func (s *Subscription) processModelEvent(event *rescache.ResourceEvent) { // Check for removing changed references after adding references to avoid unsubscribing to // a resource that is going to be subscribed again because it has moved between properties. for k := range ch { - if ov, ok := old[k]; ok && ov.Type == codec.ValueTypeResource { + if ov, ok := old[k]; ok && ov.Type == codec.ValueTypeReference { s.removeReference(ov.RID) } } // Quick exit if there are no new unsent subscriptions if subs == nil { - s.c.Send(rpc.NewEvent(s.rid, event.Event, rpc.ChangeEvent{Values: event.Changed})) + // Legacy behavior + if s.c.ProtocolVersion() < versionSoftResourceReferenceAndDataValue { + s.c.Send(rpc.NewEvent(s.rid, event.Event, rpc.ChangeEvent{Values: rescache.Legacy120ValueMap(event.Changed)})) + } else { + s.c.Send(rpc.NewEvent(s.rid, event.Event, rpc.ChangeEvent{Values: event.Changed})) + } return } @@ -633,10 +693,19 @@ func (s *Subscription) processModelEvent(event *rescache.ResourceEvent) { } r := &rpc.Resources{} - for _, sub := range subs { - sub.populateResources(r) + + // Legacy behavior + if s.c.ProtocolVersion() < versionSoftResourceReferenceAndDataValue { + for _, sub := range subs { + sub.populateResourcesLegacy(r) + } + s.c.Send(rpc.NewEvent(s.rid, event.Event, rpc.ChangeEvent{Values: rescache.Legacy120ValueMap(event.Changed), Resources: r})) + } else { + for _, sub := range subs { + sub.populateResources(r) + } + s.c.Send(rpc.NewEvent(s.rid, event.Event, rpc.ChangeEvent{Values: event.Changed, Resources: r})) } - s.c.Send(rpc.NewEvent(s.rid, event.Event, rpc.ChangeEvent{Values: event.Changed, Resources: r})) for _, sub := range subs { sub.ReleaseRPCResources() } diff --git a/server/version.go b/server/version.go index 5558a7c..b104ccf 100644 --- a/server/version.go +++ b/server/version.go @@ -1,6 +1,12 @@ package server -// Last protocol version where a specific feature was not supported. +// Protocol versions const ( - versionCallResourceResponse = 1001001 + versionLatest = 1002001 // MAJOR * 1000000 + MINOR * 1000 + PATCH + versionLegacy = 1001001 +) + +const ( + versionCallResourceResponse = 1002000 + versionSoftResourceReferenceAndDataValue = 1002001 ) diff --git a/server/wsConn.go b/server/wsConn.go index e2307f5..aff5d60 100644 --- a/server/wsConn.go +++ b/server/wsConn.go @@ -36,12 +36,6 @@ type wsConn struct { mu sync.Mutex } -// Protocol versions -const ( - legacyProtocol = 1001001 // MAJOR * 1000000 + MINOR * 1000 + PATCH - latestProtocol = 1999999 -) - var ( errInvalidNewResourceResponse = reserr.InternalError(errors.New("non-resource response on new request")) ) @@ -412,7 +406,7 @@ func (c *wsConn) handleCallAuthResponse(result json.RawMessage, refRID string, e } // Legacy behavior - if c.protocolVer <= versionCallResourceResponse { + if c.protocolVer < versionCallResourceResponse { // Handle resource response by just returning the resource ID without subscription if refRID != "" { cb(rpc.CallResourceResult{RID: refRID}, nil) @@ -467,8 +461,8 @@ func (c *wsConn) handleResourceResult(refRID string, cb func(result interface{}, }) } -func (c *wsConn) UnsubscribeResource(rid string, cb func(ok bool)) { - cb(c.UnsubscribeByRID(rid)) +func (c *wsConn) UnsubscribeResource(rid string, count int, cb func(ok bool)) { + cb(c.UnsubscribeByRID(rid, count)) } func (c *wsConn) subscribe(rid string, direct bool) (*Subscription, error) { @@ -507,17 +501,17 @@ func (c *wsConn) Unsubscribe(sub *Subscription, direct bool, count int, tryDelet c.removeCount(sub, direct, count, tryDelete) } -func (c *wsConn) UnsubscribeByRID(rid string) bool { +func (c *wsConn) UnsubscribeByRID(rid string, count int) bool { if c.disposing { return false } sub, ok := c.subs[rid] - if !ok || sub.direct == 0 { + if !ok || sub.direct < count { return false } - c.removeCount(sub, true, 1, true) + c.removeCount(sub, true, count, true) return true } diff --git a/server/wsHandler.go b/server/wsHandler.go index bc57b39..2f3529b 100644 --- a/server/wsHandler.go +++ b/server/wsHandler.go @@ -48,7 +48,7 @@ func (s *Service) wsHandler(w http.ResponseWriter, r *http.Request) { return } - conn := s.newWSConn(ws, r, legacyProtocol) + conn := s.newWSConn(ws, r, versionLegacy) if conn == nil { return } diff --git a/test/01subscribe_test.go b/test/01subscribe_test.go index f155315..5189460 100644 --- a/test/01subscribe_test.go +++ b/test/01subscribe_test.go @@ -34,7 +34,7 @@ func TestResponseOnPrimitiveModelRetrieval(t *testing.T) { modelGetResponse := json.RawMessage(`{"model":` + model + `}`) fullAccess := json.RawMessage(`{"get":true}`) noAccess := json.RawMessage(`{"get":false}`) - modelClientResponse := json.RawMessage(`{"models":{"test.model":` + model + `}}`) + modelClientResponse := json.RawMessage(`{"models":{"test.resource":` + model + `}}`) // *reserr.Error implies an error response. requestTimeout implies a timeout. Otherwise success. tbl := []struct { @@ -86,7 +86,6 @@ func TestResponseOnPrimitiveModelRetrieval(t *testing.T) { // Invalid get model or collection response {json.RawMessage(`{"model":["with","array","data"]}`), fullAccess, reserr.CodeInternalError}, {json.RawMessage(`{"collection":{"with":"model data"}}`), fullAccess, reserr.CodeInternalError}, - {json.RawMessage(`{"collection":[1,2],"model":{"and":"model"}}`), fullAccess, reserr.CodeInternalError}, {json.RawMessage(`{"model":{"array":[1,2]}}`), fullAccess, reserr.CodeInternalError}, {json.RawMessage(`{"model":{"prop":{"action":"delete"}}}`), fullAccess, reserr.CodeInternalError}, {json.RawMessage(`{"model":{"prop":{"action":"unknown"}}}`), fullAccess, reserr.CodeInternalError}, @@ -97,6 +96,8 @@ func TestResponseOnPrimitiveModelRetrieval(t *testing.T) { {json.RawMessage(`{"collection":["prop",{"action":"delete"}]}`), fullAccess, reserr.CodeInternalError}, {json.RawMessage(`{"collection":["prop",{"action":"unknown"}]}`), fullAccess, reserr.CodeInternalError}, {json.RawMessage(`{"collection":["prop",{"unknown":"property"}]}`), fullAccess, reserr.CodeInternalError}, + // Multiple resource types + {json.RawMessage(`{"model":{"foo":"bar"},"collection":[1,2,3]}`), fullAccess, reserr.CodeInternalError}, // Invalid get error response {[]byte(`{"error":[]}`), fullAccess, reserr.CodeInternalError}, {[]byte(`{"error":{"message":"missing code"}}`), fullAccess, ""}, @@ -119,16 +120,16 @@ func TestResponseOnPrimitiveModelRetrieval(t *testing.T) { var creq *ClientRequest switch method { case "get": - creq = c.Request("get.test.model", nil) + creq = c.Request("get.test.resource", nil) case "subscribe": - creq = c.Request("subscribe.test.model", nil) + creq = c.Request("subscribe.test.resource", nil) } mreqs := s.GetParallelRequests(t, 2) var req *Request if getFirst { // Send get response - req = mreqs.GetRequest(t, "get.test.model") + req = mreqs.GetRequest(t, "get.test.resource") if l.GetResponse == requestTimeout { req.Timeout() } else if err, ok := l.GetResponse.(*reserr.Error); ok { @@ -141,7 +142,7 @@ func TestResponseOnPrimitiveModelRetrieval(t *testing.T) { } // Send access response - req = mreqs.GetRequest(t, "access.test.model") + req = mreqs.GetRequest(t, "access.test.resource") if l.AccessResponse == requestTimeout { req.Timeout() } else if err, ok := l.AccessResponse.(*reserr.Error); ok { @@ -154,7 +155,7 @@ func TestResponseOnPrimitiveModelRetrieval(t *testing.T) { if !getFirst { // Send get response - req := mreqs.GetRequest(t, "get.test.model") + req := mreqs.GetRequest(t, "get.test.resource") if l.GetResponse == nil { req.Timeout() } else if err, ok := l.GetResponse.(*reserr.Error); ok { @@ -185,118 +186,152 @@ func TestResponseOnPrimitiveModelRetrieval(t *testing.T) { func TestSubscribe(t *testing.T) { event := json.RawMessage(`{"foo":"bar"}`) - responses := map[string][]string{ - // Model responses - "test.model": {"test.model"}, - "test.model.parent": {"test.model.parent", "test.model"}, - "test.model.grandparent": {"test.model.grandparent", "test.model.parent", "test.model"}, - "test.model.secondparent": {"test.model.secondparent", "test.model"}, - "test.model.brokenchild": {"test.model.brokenchild", "test.err.notFound"}, - // Cyclic model responses - "test.m.a": {"test.m.a"}, - "test.m.b": {"test.m.b", "test.m.c"}, - "test.m.d": {"test.m.d", "test.m.e", "test.m.f"}, - "test.m.g": {"test.m.d", "test.m.e", "test.m.f", "test.m.g"}, - "test.m.h": {"test.m.d", "test.m.e", "test.m.f", "test.m.h"}, - // Collection responses - "test.collection": {"test.collection"}, - "test.collection.parent": {"test.collection.parent", "test.collection"}, - "test.collection.grandparent": {"test.collection.grandparent", "test.collection.parent", "test.collection"}, - "test.collection.secondparent": {"test.collection.secondparent", "test.collection"}, - "test.collection.brokenchild": {"test.collection.brokenchild", "test.err.notFound"}, - // Cyclic collection responses - "test.c.a": {"test.c.a"}, - "test.c.b": {"test.c.b", "test.c.c"}, - "test.c.d": {"test.c.d", "test.c.e", "test.c.f"}, - "test.c.g": {"test.c.d", "test.c.e", "test.c.f", "test.c.g"}, - "test.c.h": {"test.c.d", "test.c.e", "test.c.f", "test.c.h"}, + responses := map[string]map[string][]struct { + RID string + Resource *resource + }{ + versionLatest: { + // Model responses + "test.model": {{"test.model", nil}}, + "test.model.parent": {{"test.model.parent", nil}, {"test.model", nil}}, + "test.model.grandparent": {{"test.model.grandparent", nil}, {"test.model.parent", nil}, {"test.model", nil}}, + "test.model.secondparent": {{"test.model.secondparent", nil}, {"test.model", nil}}, + "test.model.brokenchild": {{"test.model.brokenchild", nil}, {"test.err.notFound", nil}}, + "test.model.soft": {{"test.model.soft", nil}}, + "test.model.soft.parent": {{"test.model.soft.parent", nil}, {"test.model.soft", nil}}, + "test.model.data": {{"test.model.data", &resource{typeModel, `{"name":"data","primitive":12,"object":{"data":{"foo":["bar"]}},"array":{"data":[{"foo":"bar"}]}}`, nil}}}, + "test.model.data.parent": {{"test.model.data.parent", nil}, {"test.model.data", &resource{typeModel, `{"name":"data","primitive":12,"object":{"data":{"foo":["bar"]}},"array":{"data":[{"foo":"bar"}]}}`, nil}}}, + // Cyclic model responses + "test.m.a": {{"test.m.a", nil}}, + "test.m.b": {{"test.m.b", nil}, {"test.m.c", nil}}, + "test.m.d": {{"test.m.d", nil}, {"test.m.e", nil}, {"test.m.f", nil}}, + "test.m.g": {{"test.m.d", nil}, {"test.m.e", nil}, {"test.m.f", nil}, {"test.m.g", nil}}, + "test.m.h": {{"test.m.d", nil}, {"test.m.e", nil}, {"test.m.f", nil}, {"test.m.h", nil}}, + // Collection responses + "test.collection": {{"test.collection", nil}}, + "test.collection.parent": {{"test.collection.parent", nil}, {"test.collection", nil}}, + "test.collection.grandparent": {{"test.collection.grandparent", nil}, {"test.collection.parent", nil}, {"test.collection", nil}}, + "test.collection.secondparent": {{"test.collection.secondparent", nil}, {"test.collection", nil}}, + "test.collection.brokenchild": {{"test.collection.brokenchild", nil}, {"test.err.notFound", nil}}, + "test.collection.soft": {{"test.collection.soft", nil}}, + "test.collection.soft.parent": {{"test.collection.soft.parent", nil}, {"test.collection.soft", nil}}, + "test.collection.data": {{"test.collection.data", &resource{typeCollection, `["data",12,{"data":{"foo":["bar"]}},{"data":[{"foo":"bar"}]}]`, nil}}}, + "test.collection.data.parent": {{"test.collection.data.parent", nil}, {"test.collection.data", &resource{typeCollection, `["data",12,{"data":{"foo":["bar"]}},{"data":[{"foo":"bar"}]}]`, nil}}}, + // Cyclic collection responses + "test.c.a": {{"test.c.a", nil}}, + "test.c.b": {{"test.c.b", nil}, {"test.c.c", nil}}, + "test.c.d": {{"test.c.d", nil}, {"test.c.e", nil}, {"test.c.f", nil}}, + "test.c.g": {{"test.c.d", nil}, {"test.c.e", nil}, {"test.c.f", nil}, {"test.c.g", nil}}, + "test.c.h": {{"test.c.d", nil}, {"test.c.e", nil}, {"test.c.f", nil}, {"test.c.h", nil}}, + }, + "1.2.0": { + // Model responses + "test.model.soft": {{"test.model.soft", &resource{typeModel, `{"name":"soft","child":"test.model"}`, nil}}}, + "test.model.soft.parent": {{"test.model.soft.parent", nil}, {"test.model.soft", &resource{typeModel, `{"name":"soft","child":"test.model"}`, nil}}}, + "test.model.data": {{"test.model.data", &resource{typeModel, `{"name":"data","primitive":12,"object":"[Data]","array":"[Data]"}`, nil}}}, + "test.model.data.parent": {{"test.model.data.parent", nil}, {"test.model.data", &resource{typeModel, `{"name":"data","primitive":12,"object":"[Data]","array":"[Data]"}`, nil}}}, + // Collection responses + "test.collection.soft": {{"test.collection.soft", &resource{typeCollection, `["soft","test.collection"]`, nil}}}, + "test.collection.soft.parent": {{"test.collection.soft.parent", nil}, {"test.collection.soft", &resource{typeCollection, `["soft","test.collection"]`, nil}}}, + "test.collection.data": {{"test.collection.data", &resource{typeCollection, `["data",12,"[Data]","[Data]"]`, nil}}}, + "test.collection.data.parent": {{"test.collection.data.parent", nil}, {"test.collection.data", &resource{typeCollection, `["data",12,"[Data]","[Data]"]`, nil}}}, + }, } - for i, l := range sequenceTable { - runNamedTest(t, fmt.Sprintf("#%d", i+1), func(s *Session) { - var creq *ClientRequest - var req *Request + for _, set := range sequenceSets { + for i, l := range set.Table { + runNamedTest(t, fmt.Sprintf("#%d for client version %s", i+1, set.Version), func(s *Session) { + var creq *ClientRequest + var req *Request - c := s.Connect() + c := s.ConnectWithVersion(set.Version) - creqs := make(map[string]*ClientRequest) - reqs := make(map[string]*Request) - sentResources := make(map[string]bool) + creqs := make(map[string]*ClientRequest) + reqs := make(map[string]*Request) + sentResources := make(map[string]bool) - for _, ev := range l { - switch ev.Event { - case "subscribe": - creqs[ev.RID] = c.Request("subscribe."+ev.RID, nil) - case "access": - for req = reqs["access."+ev.RID]; req == nil; req = reqs["access."+ev.RID] { - treq := s.GetRequest(t) - reqs[treq.Subject] = treq - } - req.RespondSuccess(json.RawMessage(`{"get":true}`)) - case "accessDenied": - for req = reqs["access."+ev.RID]; req == nil; req = reqs["access."+ev.RID] { - treq := s.GetRequest(t) - reqs[treq.Subject] = treq - } - req.RespondSuccess(json.RawMessage(`{"get":false}`)) - case "get": - for req = reqs["get."+ev.RID]; req == nil; req = reqs["get."+ev.RID] { - req = s.GetRequest(t) - reqs[req.Subject] = req - } - rsrc := resources[ev.RID] - switch rsrc.typ { - case typeModel: - req.RespondSuccess(json.RawMessage(`{"model":` + rsrc.data + `}`)) - case typeCollection: - req.RespondSuccess(json.RawMessage(`{"collection":` + rsrc.data + `}`)) - case typeError: - req.RespondError(rsrc.err) - } - case "response": - creq = creqs[ev.RID] - rids := responses[ev.RID] - models := make(map[string]json.RawMessage) - collections := make(map[string]json.RawMessage) - errors := make(map[string]*reserr.Error) - for _, rid := range rids { - if sentResources[rid] { - continue + for _, ev := range l { + switch ev.Event { + case "subscribe": + creqs[ev.RID] = c.Request("subscribe."+ev.RID, nil) + case "access": + for req = reqs["access."+ev.RID]; req == nil; req = reqs["access."+ev.RID] { + treq := s.GetRequest(t) + reqs[treq.Subject] = treq + } + req.RespondSuccess(json.RawMessage(`{"get":true}`)) + case "accessDenied": + for req = reqs["access."+ev.RID]; req == nil; req = reqs["access."+ev.RID] { + treq := s.GetRequest(t) + reqs[treq.Subject] = treq } - rsrc := resources[rid] + req.RespondSuccess(json.RawMessage(`{"get":false}`)) + case "get": + for req = reqs["get."+ev.RID]; req == nil; req = reqs["get."+ev.RID] { + req = s.GetRequest(t) + reqs[req.Subject] = req + } + rsrc := resources[ev.RID] switch rsrc.typ { case typeModel: - models[rid] = json.RawMessage(rsrc.data) + req.RespondSuccess(json.RawMessage(`{"model":` + rsrc.data + `}`)) case typeCollection: - collections[rid] = json.RawMessage(rsrc.data) + req.RespondSuccess(json.RawMessage(`{"collection":` + rsrc.data + `}`)) case typeError: - errors[rid] = rsrc.err + req.RespondError(rsrc.err) } - sentResources[rid] = true - } - m := make(map[string]interface{}) - if len(models) > 0 { - m["models"] = models - } - if len(collections) > 0 { - m["collections"] = collections - } - if len(errors) > 0 { - m["errors"] = errors + case "response": + creq = creqs[ev.RID] + respResources := responses[set.Version][ev.RID] + models := make(map[string]json.RawMessage) + collections := make(map[string]json.RawMessage) + errors := make(map[string]*reserr.Error) + for _, rr := range respResources { + if sentResources[rr.RID] { + continue + } + var rsrc resource + if rr.Resource == nil { + rsrc = resources[rr.RID] + } else { + rsrc = *rr.Resource + } + switch rsrc.typ { + case typeModel: + models[rr.RID] = json.RawMessage(rsrc.data) + case typeCollection: + collections[rr.RID] = json.RawMessage(rsrc.data) + case typeError: + errors[rr.RID] = rsrc.err + } + sentResources[rr.RID] = true + } + m := make(map[string]interface{}) + if len(models) > 0 { + m["models"] = models + } + if len(collections) > 0 { + m["collections"] = collections + } + if len(errors) > 0 { + m["errors"] = errors + } + creq.GetResponse(t).AssertResult(t, m) + case "errorResponse": + creq = creqs[ev.RID] + creq.GetResponse(t).AssertIsError(t) + case "event": + s.ResourceEvent(ev.RID, "custom", event) + c.GetEvent(t).Equals(t, ev.RID+".custom", event) + case "noevent": + s.ResourceEvent(ev.RID, "custom", event) + c.AssertNoEvent(t, ev.RID) + case "nosubscription": + s.NoSubscriptions(t, ev.RID) } - creq.GetResponse(t).AssertResult(t, m) - case "errorResponse": - creq = creqs[ev.RID] - creq.GetResponse(t).AssertIsError(t) - case "event": - s.ResourceEvent(ev.RID, "custom", event) - c.GetEvent(t).Equals(t, ev.RID+".custom", event) - case "noevent": - s.ResourceEvent(ev.RID, "custom", event) - c.AssertNoEvent(t, ev.RID) } - } - }) + }) + } } } @@ -337,3 +372,47 @@ func TestSubscribe_WithCIDPlaceholder_ReplacesCID(t *testing.T) { c.GetEvent(t).AssertEventName(t, "test.{cid}.model.custom") }) } + +func TestSubscribe_MultipleClientsSubscribingResource_FetchedFromCache(t *testing.T) { + tbl := []struct { + RID string + }{ + {"test.model"}, + {"test.collection"}, + } + for i, l := range tbl { + runNamedTest(t, fmt.Sprintf("#%d", i+1), func(s *Session) { + rid := l.RID + c1 := s.Connect() + subscribeToResource(t, s, c1, rid) + + // Connect with second client + c2 := s.Connect() + // Send subscribe request + c2req := c2.Request("subscribe."+rid, nil) + s.GetRequest(t). + AssertSubject(t, "access."+rid). + RespondSuccess(json.RawMessage(`{"get":true}`)) + // Handle resource and validate client response + rsrc, ok := resources[rid] + if !ok { + panic("no resource named " + rid) + } + var r string + if rsrc.typ == typeError { + b, _ := json.Marshal(rsrc.err) + r = string(b) + } else { + r = rsrc.data + } + switch rsrc.typ { + case typeModel: + c2req.GetResponse(t).AssertResult(t, json.RawMessage(`{"models":{"`+rid+`":`+r+`}}`)) + case typeCollection: + c2req.GetResponse(t).AssertResult(t, json.RawMessage(`{"collections":{"`+rid+`":`+r+`}}`)) + default: + panic("invalid type") + } + }) + } +} diff --git a/test/03unsubscribe_test.go b/test/03unsubscribe_test.go index ec7f8a3..e8fba66 100644 --- a/test/03unsubscribe_test.go +++ b/test/03unsubscribe_test.go @@ -2,6 +2,7 @@ package test import ( "encoding/json" + "fmt" "testing" "github.com/resgateio/resgate/server/reserr" @@ -159,3 +160,109 @@ func TestUnsubscribeOnOverlappingLinkedCollection(t *testing.T) { c.GetEvent(t).Equals(t, "test.collection.secondparent.custom", event) }) } + +func TestUnsubscribe_FollowedByResourceResponse_IncludesResource(t *testing.T) { + for useCount := true; useCount; useCount = false { + runNamedTest(t, fmt.Sprintf("with useCount set to %+v", useCount), func(s *Session) { + c := s.Connect() + model := resourceData("test.model") + + // Send subscribe request + creq := c.Request("subscribe.test.model", nil) + // Handle model get and access request + mreqs := s.GetParallelRequests(t, 2) + mreqs.GetRequest(t, "get.test.model").RespondSuccess(json.RawMessage(`{"model":` + model + `}`)) + req := mreqs.GetRequest(t, "access.test.model") + req.RespondSuccess(json.RawMessage(`{"get":true}`)) + + // Validate client response and validate + creq.GetResponse(t).AssertResult(t, json.RawMessage(`{"models":{"test.model":`+model+`}}`)) + + // Send client request + creq = c.Request("call.test.getModel", nil) + req = s.GetRequest(t) + req.AssertSubject(t, "access.test") + req.RespondSuccess(json.RawMessage(`{"get":true,"call":"*"}`)) + // Get call request + req = s.GetRequest(t) + req.AssertSubject(t, "call.test.getModel") + req.RespondResource("test.model") + // Validate client response + cresp := creq.GetResponse(t) + cresp.AssertResult(t, json.RawMessage(`{"rid":"test.model"}`)) + + // Call unsubscribe + if useCount { + c.Request("unsubscribe.test.model", json.RawMessage(`{"count":2}`)).GetResponse(t) + } else { + c.Request("unsubscribe.test.model", json.RawMessage(`{}`)).GetResponse(t) + c.Request("unsubscribe.test.model", nil).GetResponse(t) + } + + // Send client request + creq = c.Request("call.test.getModel", nil) + req = s.GetRequest(t) + req.AssertSubject(t, "access.test") + req.RespondSuccess(json.RawMessage(`{"get":true,"call":"*"}`)) + // Get call request + req = s.GetRequest(t) + req.AssertSubject(t, "call.test.getModel") + req.RespondResource("test.model") + // Access request + req = s.GetRequest(t) + req.AssertSubject(t, "access.test.model") + req.RespondSuccess(json.RawMessage(`{"get":true}`)) + // Validate client response + cresp = creq.GetResponse(t) + cresp.AssertResult(t, json.RawMessage(`{"rid":"test.model","models":{"test.model":`+model+`}}`)) + }) + } +} + +func TestUnsubscribe_WithCount_UnsubscribesModel(t *testing.T) { + runTest(t, func(s *Session) { + event := json.RawMessage(`{"foo":"bar"}`) + + c := s.Connect() + subscribeToTestModel(t, s, c) + + // Call unsubscribe + c.Request("unsubscribe.test.model", json.RawMessage(`{"count":1}`)).GetResponse(t) + + // Send event on model and validate no event was sent to client + s.ResourceEvent("test.model", "custom", event) + c.AssertNoEvent(t, "test.model") + }) +} + +func TestUnsubscribe_WithInvalidPayload_DoesNotUnsubscribesModel(t *testing.T) { + tbl := []struct { + Payload interface{} + ErrorCode string + }{ + {json.RawMessage(`[]`), "system.invalidParams"}, + {json.RawMessage(`{"count":"foo"}`), "system.invalidParams"}, + {json.RawMessage(`{"count":true}`), "system.invalidParams"}, + {json.RawMessage(`{"count":0}`), "system.invalidParams"}, + {json.RawMessage(`{"count":-1}`), "system.invalidParams"}, + {json.RawMessage(`{"count":2}`), "system.noSubscription"}, + } + + event := json.RawMessage(`{"foo":"bar"}`) + + for i, l := range tbl { + runNamedTest(t, fmt.Sprintf("#%d", i+1), func(s *Session) { + c := s.Connect() + subscribeToTestModel(t, s, c) + + // Call unsubscribe + c.Request("unsubscribe.test.model", l.Payload). + GetResponse(t). + AssertErrorCode(t, l.ErrorCode) + + // Send event on model and validate it is still subscribed + s.ResourceEvent("test.model", "custom", event) + c.GetEvent(t).AssertEventName(t, "test.model.custom") + }) + } +} diff --git a/test/07model_event_test.go b/test/07model_event_test.go index b6071b5..5d4a609 100644 --- a/test/07model_event_test.go +++ b/test/07model_event_test.go @@ -46,27 +46,38 @@ func TestChangeEventPriorToGetResponseIsDiscarded(t *testing.T) { // Test change event effect on cached model func TestChangeEventOnCachedModel(t *testing.T) { tbl := []struct { + RID string // RID of resource to subscribe to ChangeEvent string // Change event to send (raw JSON) ExpectedChangeEvent string // Expected event sent to client (raw JSON. Empty means none) ExpectedModel string // Expected model after event (raw JSON) ExpectedErrors int }{ - {`{"values":{"string":"bar","int":-12}}`, `{"values":{"string":"bar","int":-12}}`, `{"string":"bar","int":-12,"bool":true,"null":null}`, 0}, - {`{"values":{"string":"bar"}}`, `{"values":{"string":"bar"}}`, `{"string":"bar","int":42,"bool":true,"null":null}`, 0}, - {`{"values":{"int":-12}}`, `{"values":{"int":-12}}`, `{"string":"foo","int":-12,"bool":true,"null":null}`, 0}, - {`{"values":{"new":false}}`, `{"values":{"new":false}}`, `{"string":"foo","int":42,"bool":true,"null":null,"new":false}`, 0}, - {`{"values":{"int":{"action":"delete"}}}`, `{"values":{"int":{"action":"delete"}}}`, `{"string":"foo","bool":true,"null":null}`, 0}, + {"test.model", `{"values":{"string":"bar","int":-12}}`, `{"values":{"string":"bar","int":-12}}`, `{"string":"bar","int":-12,"bool":true,"null":null}`, 0}, + {"test.model", `{"values":{"string":"bar"}}`, `{"values":{"string":"bar"}}`, `{"string":"bar","int":42,"bool":true,"null":null}`, 0}, + {"test.model", `{"values":{"int":-12}}`, `{"values":{"int":-12}}`, `{"string":"foo","int":-12,"bool":true,"null":null}`, 0}, + {"test.model", `{"values":{"new":false}}`, `{"values":{"new":false}}`, `{"string":"foo","int":42,"bool":true,"null":null,"new":false}`, 0}, + {"test.model", `{"values":{"int":{"action":"delete"}}}`, `{"values":{"int":{"action":"delete"}}}`, `{"string":"foo","bool":true,"null":null}`, 0}, + {"test.model", `{"values":{"soft":{"rid":"test.model.soft","soft":true}}}`, `{"values":{"soft":{"rid":"test.model.soft","soft":true}}}`, `{"string":"foo","int":42,"bool":true,"null":null,"soft":{"rid":"test.model.soft","soft":true}}`, 0}, + {"test.model.soft", `{"values":{"child":null}}`, `{"values":{"child":null}}`, `{"name":"soft","child":null}`, 0}, + {"test.model.soft", `{"values":{"child":{"action":"delete"}}}`, `{"values":{"child":{"action":"delete"}}}`, `{"name":"soft"}`, 0}, + {"test.model.data", `{"values":{"primitive":{"data":13}}}`, `{"values":{"primitive":13}}`, `{"name":"data","primitive":13,"object":{"data":{"foo":["bar"]}},"array":{"data":[{"foo":"bar"}]}}`, 0}, + {"test.model.data", `{"values":{"object":{"data":{"foo":["baz"]}}}}`, `{"values":{"object":{"data":{"foo":["baz"]}}}}`, `{"name":"data","primitive":12,"object":{"data":{"foo":["baz"]}},"array":{"data":[{"foo":"bar"}]}}`, 0}, + {"test.model.data", `{"values":{"array":{"data":[{"foo":"baz"}]}}}`, `{"values":{"array":{"data":[{"foo":"baz"}]}}}`, `{"name":"data","primitive":12,"object":{"data":{"foo":["bar"]}},"array":{"data":[{"foo":"baz"}]}}`, 0}, // Unchanged values - {`{"values":{}}`, "", `{"string":"foo","int":42,"bool":true,"null":null}`, 0}, - {`{"values":{"string":"foo"}}`, "", `{"string":"foo","int":42,"bool":true,"null":null}`, 0}, - {`{"values":{"string":"foo","int":42}}`, "", `{"string":"foo","int":42,"bool":true,"null":null}`, 0}, - {`{"values":{"invalid":{"action":"delete"}}}`, "", `{"string":"foo","int":42,"bool":true,"null":null}`, 0}, - {`{"values":{"null":null,"string":"bar"}}`, `{"values":{"string":"bar"}}`, `{"string":"bar","int":42,"bool":true,"null":null}`, 0}, + {"test.model", `{"values":{}}`, "", `{"string":"foo","int":42,"bool":true,"null":null}`, 0}, + {"test.model", `{"values":{"string":"foo"}}`, "", `{"string":"foo","int":42,"bool":true,"null":null}`, 0}, + {"test.model", `{"values":{"string":"foo","int":42}}`, "", `{"string":"foo","int":42,"bool":true,"null":null}`, 0}, + {"test.model", `{"values":{"invalid":{"action":"delete"}}}`, "", `{"string":"foo","int":42,"bool":true,"null":null}`, 0}, + {"test.model", `{"values":{"null":null,"string":"bar"}}`, `{"values":{"string":"bar"}}`, `{"string":"bar","int":42,"bool":true,"null":null}`, 0}, + {"test.model.soft", `{"values":{"child":{"rid":"test.model","soft":true}}}`, "", `{"name":"soft","child":{"rid":"test.model","soft":true}}`, 0}, + {"test.model.data", `{"values":{}}`, "", `{"name":"data","primitive":12,"object":{"data":{"foo":["bar"]}},"array":{"data":[{"foo":"bar"}]}}`, 0}, + {"test.model.data", `{"values":{"primitive":12,"object":{"data":{"foo":["bar"]}},"array":{"data":[{"foo":"bar"}]}}}`, "", `{"name":"data","primitive":12,"object":{"data":{"foo":["bar"]}},"array":{"data":[{"foo":"bar"}]}}`, 0}, + {"test.model", `{"values":{"null":{"data":null}}}`, "", `{"string":"foo","int":42,"bool":true,"null":null}`, 0}, // Model change event v1.0 legacy behavior - {`{"string":"bar","int":-12}`, `{"values":{"string":"bar","int":-12}}`, `{"string":"bar","int":-12,"bool":true,"null":null}`, 1}, - {`{"string":"bar"}`, `{"values":{"string":"bar"}}`, `{"string":"bar","int":42,"bool":true,"null":null}`, 1}, + {"test.model", `{"string":"bar","int":-12}`, `{"values":{"string":"bar","int":-12}}`, `{"string":"bar","int":-12,"bool":true,"null":null}`, 1}, + {"test.model", `{"string":"bar"}`, `{"values":{"string":"bar"}}`, `{"string":"bar","int":42,"bool":true,"null":null}`, 1}, } for i, l := range tbl { @@ -75,31 +86,31 @@ func TestChangeEventOnCachedModel(t *testing.T) { var creq *ClientRequest c := s.Connect() - subscribeToTestModel(t, s, c) + subscribeToResource(t, s, c, l.RID) // Send event on model and validate client event - s.ResourceEvent("test.model", "change", json.RawMessage(l.ChangeEvent)) + s.ResourceEvent(l.RID, "change", json.RawMessage(l.ChangeEvent)) if l.ExpectedChangeEvent == "" { - c.AssertNoEvent(t, "test.model.change") + c.AssertNoEvent(t, l.RID+".change") } else { - c.GetEvent(t).Equals(t, "test.model.change", json.RawMessage(l.ExpectedChangeEvent)) + c.GetEvent(t).Equals(t, l.RID+".change", json.RawMessage(l.ExpectedChangeEvent)) } if sameClient { - c.Request("unsubscribe.test.model", nil).GetResponse(t) + c.Request("unsubscribe."+l.RID, nil).GetResponse(t) // Subscribe a second time - creq = c.Request("subscribe.test.model", nil) + creq = c.Request("subscribe."+l.RID, nil) } else { c2 := s.Connect() // Subscribe a second time - creq = c2.Request("subscribe.test.model", nil) + creq = c2.Request("subscribe."+l.RID, nil) } // Handle model access request - s.GetRequest(t).AssertSubject(t, "access.test.model").RespondSuccess(json.RawMessage(`{"get":true}`)) + s.GetRequest(t).AssertSubject(t, "access."+l.RID).RespondSuccess(json.RawMessage(`{"get":true}`)) // Validate client response - creq.GetResponse(t).AssertResult(t, json.RawMessage(`{"models":{"test.model":`+l.ExpectedModel+`}}`)) + creq.GetResponse(t).AssertResult(t, json.RawMessage(`{"models":{"`+l.RID+`":`+l.ExpectedModel+`}}`)) s.AssertErrorsLogged(t, l.ExpectedErrors) }) } @@ -179,3 +190,48 @@ func TestChangeEventWithChangedResourceReference(t *testing.T) { c.AssertNoEvent(t, "test.model") }) } + +// Test change event with removed resource reference +func TestChangeEvent_WithResourceReferenceReplacedBySoftReference_UnsubscribesReference(t *testing.T) { + runTest(t, func(s *Session) { + c := s.Connect() + subscribeToTestModelParent(t, s, c, false) + + // Send event on model and validate client event + s.ResourceEvent("test.model", "custom", common.CustomEvent()) + c.GetEvent(t).Equals(t, "test.model.custom", common.CustomEvent()) + + // Send event on model and validate client event + s.ResourceEvent("test.model.parent", "change", json.RawMessage(`{"values":{"child":{"rid":"test.model","soft":true}}}`)) + c.GetEvent(t).Equals(t, "test.model.parent.change", json.RawMessage(`{"values":{"child":{"rid":"test.model","soft":true}}}`)) + + // Send event on collection and validate client event is not sent to client + s.ResourceEvent("test.model", "custom", common.CustomEvent()) + c.AssertNoEvent(t, "test.model") + }) +} + +// Test change event with new resource reference +func TestChangeEvent_WithSoftReferenceReplacedByResourceReference_SubscribesReference(t *testing.T) { + model := resourceData("test.model") + + runTest(t, func(s *Session) { + c := s.Connect() + subscribeToResource(t, s, c, "test.model.soft") + + // Send event on model and validate client event + s.ResourceEvent("test.model.soft", "change", json.RawMessage(`{"values":{"child":{"rid":"test.model","soft":false}}}`)) + + // Handle model get request + s. + GetRequest(t). + AssertSubject(t, "get.test.model"). + RespondSuccess(json.RawMessage(`{"model":` + model + `}`)) + + c.GetEvent(t).Equals(t, "test.model.soft.change", json.RawMessage(`{"values":{"child":{"rid":"test.model","soft":false}},"models":{"test.model":`+model+`}}`)) + + // Send event on model and validate client event + s.ResourceEvent("test.model", "custom", common.CustomEvent()) + c.GetEvent(t).Equals(t, "test.model.custom", common.CustomEvent()) + }) +} diff --git a/test/08collection_event_test.go b/test/08collection_event_test.go index e0b4b04..cec835f 100644 --- a/test/08collection_event_test.go +++ b/test/08collection_event_test.go @@ -25,16 +25,25 @@ func TestAddAndRemoveEventOnSubscribedResource(t *testing.T) { // Test add and remove event effects on cached collection func TestAddRemoveEventsOnCachedCollection(t *testing.T) { tbl := []struct { + RID string // Resource ID EventName string // Name of the event. Either add or remove. EventPayload string // Event payload (raw JSON) ExpectedCollection string // Expected collection after event (raw JSON) + ExpectedEvent string // Expected event payload (empty means same as EventPayload) }{ - {"add", `{"idx":0,"value":"bar"}`, `["bar","foo",42,true,null]`}, - {"add", `{"idx":1,"value":"bar"}`, `["foo","bar",42,true,null]`}, - {"add", `{"idx":4,"value":"bar"}`, `["foo",42,true,null,"bar"]`}, - {"remove", `{"idx":0}`, `[42,true,null]`}, - {"remove", `{"idx":1}`, `["foo",true,null]`}, - {"remove", `{"idx":3}`, `["foo",42,true]`}, + {"test.collection", "add", `{"idx":0,"value":"bar"}`, `["bar","foo",42,true,null]`, ""}, + {"test.collection", "add", `{"idx":1,"value":"bar"}`, `["foo","bar",42,true,null]`, ""}, + {"test.collection", "add", `{"idx":4,"value":"bar"}`, `["foo",42,true,null,"bar"]`, ""}, + {"test.collection", "add", `{"idx":0,"value":{"rid":"test.collection.soft","soft":true}}`, `[{"rid":"test.collection.soft","soft":true},"foo",42,true,null]`, ""}, + {"test.collection", "add", `{"idx":0,"value":{"data":{"foo":["bar"]}}}`, `[{"data":{"foo":["bar"]}},"foo",42,true,null]`, ""}, + {"test.collection", "add", `{"idx":0,"value":{"data":12}}`, `[12,"foo",42,true,null]`, `{"idx":0,"value":12}`}, + {"test.collection", "remove", `{"idx":0}`, `[42,true,null]`, ""}, + {"test.collection", "remove", `{"idx":1}`, `["foo",true,null]`, ""}, + {"test.collection", "remove", `{"idx":3}`, `["foo",42,true]`, ""}, + {"test.collection.soft", "remove", `{"idx":1}`, `["soft"]`, ""}, + {"test.collection.data", "remove", `{"idx":1}`, `["data",{"data":{"foo":["bar"]}},{"data":[{"foo":"bar"}]}]`, ""}, + {"test.collection.data", "remove", `{"idx":2}`, `["data",12,{"data":[{"foo":"bar"}]}]`, ""}, + {"test.collection.data", "remove", `{"idx":3}`, `["data",12,{"data":{"foo":["bar"]}}]`, ""}, } for i, l := range tbl { @@ -43,74 +52,32 @@ func TestAddRemoveEventsOnCachedCollection(t *testing.T) { var creq *ClientRequest c := s.Connect() - subscribeToTestCollection(t, s, c) + subscribeToResource(t, s, c, l.RID) // Send event on collection and validate client event - s.ResourceEvent("test.collection", l.EventName, json.RawMessage(l.EventPayload)) - c.GetEvent(t).Equals(t, "test.collection."+l.EventName, json.RawMessage(l.EventPayload)) + s.ResourceEvent(l.RID, l.EventName, json.RawMessage(l.EventPayload)) + expectedEvent := l.ExpectedEvent + if expectedEvent == "" { + expectedEvent = l.EventPayload + } + c.GetEvent(t).Equals(t, l.RID+"."+l.EventName, json.RawMessage(expectedEvent)) if sameClient { - c.Request("unsubscribe.test.collection", nil).GetResponse(t) + c.Request("unsubscribe."+l.RID, nil).GetResponse(t) // Subscribe a second time - creq = c.Request("subscribe.test.collection", nil) + creq = c.Request("subscribe."+l.RID, nil) } else { c2 := s.Connect() // Subscribe a second time - creq = c2.Request("subscribe.test.collection", nil) + creq = c2.Request("subscribe."+l.RID, nil) } // Handle collection access request - s.GetRequest(t).AssertSubject(t, "access.test.collection").RespondSuccess(json.RawMessage(`{"get":true}`)) + s.GetRequest(t).AssertSubject(t, "access."+l.RID).RespondSuccess(json.RawMessage(`{"get":true}`)) // Validate client response - creq.GetResponse(t).AssertResult(t, json.RawMessage(`{"collections":{"test.collection":`+l.ExpectedCollection+`}}`)) + creq.GetResponse(t).AssertResult(t, json.RawMessage(`{"collections":{"`+l.RID+`":`+l.ExpectedCollection+`}}`)) }) } } } - -// Test add event with new resource reference -func TestAddEventWithNewResourceReference(t *testing.T) { - model := resourceData("test.model") - - runTest(t, func(s *Session) { - - c := s.Connect() - subscribeToTestCollection(t, s, c) - - // Send event on collection and validate client event - s.ResourceEvent("test.collection", "add", json.RawMessage(`{"idx":1,"value":{"rid":"test.model"}}`)) - - // Handle collection get request - s. - GetRequest(t). - AssertSubject(t, "get.test.model"). - RespondSuccess(json.RawMessage(`{"model":` + model + `}`)) - - c.GetEvent(t).Equals(t, "test.collection.add", json.RawMessage(`{"idx":1,"value":{"rid":"test.model"},"models":{"test.model":`+model+`}}`)) - - // Send event on model and validate client event - s.ResourceEvent("test.model", "custom", common.CustomEvent()) - c.GetEvent(t).Equals(t, "test.model.custom", common.CustomEvent()) - }) -} - -// Test remove event with removed resource reference -func TestRemoveEventWithRemovedResourceReference(t *testing.T) { - runTest(t, func(s *Session) { - c := s.Connect() - subscribeToTestCollectionParent(t, s, c, false) - - // Send event on collection and validate client event - s.ResourceEvent("test.collection", "custom", common.CustomEvent()) - c.GetEvent(t).Equals(t, "test.collection.custom", common.CustomEvent()) - - // Send event on collection and validate client event - s.ResourceEvent("test.collection.parent", "remove", json.RawMessage(`{"idx":1}`)) - c.GetEvent(t).Equals(t, "test.collection.parent.remove", json.RawMessage(`{"idx":1}`)) - - // Send event on collection and validate client event is not sent to client - s.ResourceEvent("test.collection", "custom", common.CustomEvent()) - c.AssertNoEvent(t, "test.collection") - }) -} diff --git a/test/11system_event_test.go b/test/11system_event_test.go index d27259e..92ce7fb 100644 --- a/test/11system_event_test.go +++ b/test/11system_event_test.go @@ -58,43 +58,59 @@ func TestSystemResetTriggersGetRequestOnCollection(t *testing.T) { }) } -// Test that a system.reset event on modified model generates change event -func TestSystemResetGeneratesChangeEventOnModel(t *testing.T) { - runTest(t, func(s *Session) { - c := s.Connect() - - // Get model - subscribeToTestModel(t, s, c) - - // Send system reset - s.SystemEvent("reset", json.RawMessage(`{"resources":["test.>"]}`)) - - // Validate a get request is sent - s.GetRequest(t).AssertSubject(t, "get.test.model").RespondSuccess(json.RawMessage(`{"model":{"string":"bar","int":42,"bool":true}}`)) - - // Validate no events are sent to client - c.GetEvent(t).AssertEventName(t, "test.model.change").AssertData(t, json.RawMessage(`{"values":{"string":"bar","null":{"action":"delete"}}}`)) - }) -} +func TestSystemReset_WithUpdatedResource_GeneratesEvents(t *testing.T) { + type event struct { + Event string + Payload string + } + tbl := []struct { + RID string + ResetResponse string + ExpectedEvents []event + }{ + {"test.model", `{"model":{"string":"foo","int":42,"bool":true,"null":null}}`, []event{}}, + {"test.model", `{"model":{"string":"bar","int":42,"bool":true}}`, []event{ + {"change", `{"values":{"string":"bar","null":{"action":"delete"}}}`}, + }}, + {"test.model", `{"model":{"string":"foo","int":42,"bool":true,"null":null,"child":{"rid":"test.model","soft":true}}}`, []event{ + {"change", `{"values":{"child":{"rid":"test.model","soft":true}}}`}, + }}, + {"test.model.soft", `{"model":{"name":"soft","child":null}}`, []event{ + {"change", `{"values":{"child":null}}`}, + }}, + {"test.collection", `{"collection":["foo",42,true,null]}`, []event{}}, + {"test.collection", `{"collection":[42,"new",true,null]}`, []event{ + {"remove", `{"idx":0}`}, + {"add", `{"idx":1,"value":"new"}`}, + }}, + {"test.collection", `{"collection":["foo",42,true,null,{"rid":"test.model","soft":true}]}`, []event{ + {"add", `{"idx":4,"value":{"rid":"test.model","soft":true}}`}, + }}, + {"test.collection.soft", `{"collection":["soft"]}`, []event{ + {"remove", `{"idx":1}`}, + }}, + } -// Test that a system.reset event on modified collection generates add and remove events -func TestSystemResetGeneratesAddRemoveEventsOnCollection(t *testing.T) { - runTest(t, func(s *Session) { - c := s.Connect() + for i, l := range tbl { + runNamedTest(t, fmt.Sprintf("#%d", i+1), func(s *Session) { + c := s.Connect() - // Get collection - subscribeToTestCollection(t, s, c) + // Get collection + subscribeToResource(t, s, c, l.RID) - // Send system reset - s.SystemEvent("reset", json.RawMessage(`{"resources":["test.>"]}`)) + // Send system reset + s.SystemEvent("reset", json.RawMessage(`{"resources":["test.>"]}`)) - // Validate a get request is sent - s.GetRequest(t).AssertSubject(t, "get.test.collection").RespondSuccess(json.RawMessage(`{"collection":[42,"new",true,null]}`)) + // Validate a get request is sent + s.GetRequest(t).AssertSubject(t, "get."+l.RID).RespondSuccess(json.RawMessage(l.ResetResponse)) - // Validate no events are sent to client - c.GetEvent(t).AssertEventName(t, "test.collection.remove").AssertData(t, json.RawMessage(`{"idx":0}`)) - c.GetEvent(t).AssertEventName(t, "test.collection.add").AssertData(t, json.RawMessage(`{"idx":1,"value":"new"}`)) - }) + for _, ev := range l.ExpectedEvents { + // Validate no events are sent to client + c.GetEvent(t).AssertEventName(t, l.RID+"."+ev.Event).AssertData(t, json.RawMessage(ev.Payload)) + } + c.AssertNoEvent(t, l.RID) + }) + } } // Test that a system.reset event triggers a re-access call on subscribed resources @@ -281,3 +297,41 @@ func TestSystemReset_InternalErrorResponseOnCollection_LogsError(t *testing.T) { s.AssertErrorsLogged(t, 1) }) } + +func TestSystemReset_MismatchingResourceTypeResponseOnModel_LogsError(t *testing.T) { + runTest(t, func(s *Session) { + c := s.Connect() + // Get model + subscribeToTestModel(t, s, c) + // Send system reset + s.SystemEvent("reset", json.RawMessage(`{"resources":["test.>"]}`)) + // Respond to get request with mismatching type + s.GetRequest(t).AssertSubject(t, "get.test.model").RespondSuccess(json.RawMessage(`{"collection":["foo",42,true,null]}`)) + // Validate no delete event is sent to client + c.AssertNoEvent(t, "test.model") + // Validate subsequent events are sent to client + s.ResourceEvent("test.model", "custom", common.CustomEvent()) + c.GetEvent(t).Equals(t, "test.model.custom", common.CustomEvent()) + // Assert error is logged + s.AssertErrorsLogged(t, 1) + }) +} + +func TestSystemReset_MismatchingResourceTypeResponseOnCollection_LogsError(t *testing.T) { + runTest(t, func(s *Session) { + c := s.Connect() + // Get collection + subscribeToTestCollection(t, s, c) + // Send system reset + s.SystemEvent("reset", json.RawMessage(`{"resources":["test.>"]}`)) + // Respond to get request with mismatching type + s.GetRequest(t).AssertSubject(t, "get.test.collection").RespondSuccess(json.RawMessage(`{"model":{"string":"foo","int":42,"bool":true,"null":null}}`)) + // Validate no delete event is sent to client + c.AssertNoEvent(t, "test.collection") + // Validate subsequent events are sent to client + s.ResourceEvent("test.collection", "custom", common.CustomEvent()) + c.GetEvent(t).Equals(t, "test.collection.custom", common.CustomEvent()) + // Assert error is logged + s.AssertErrorsLogged(t, 1) + }) +} diff --git a/test/14http_get_test.go b/test/14http_get_test.go index d9973b1..e09079b 100644 --- a/test/14http_get_test.go +++ b/test/14http_get_test.go @@ -61,6 +61,10 @@ func TestHTTPGet(t *testing.T) { "test.model.grandparent": `{"name":"grandparent","child":{"href":"/api/test/model/parent","model":{"name":"parent","child":{"href":"/api/test/model","model":` + resourceData("test.model") + `}}}}`, "test.model.secondparent": `{"name":"secondparent","child":{"href":"/api/test/model","model":` + resourceData("test.model") + `}}`, "test.model.brokenchild": `{"name":"brokenchild","child":{"href":"/api/test/err/notFound","error":` + resourceData("test.err.notFound") + `}}`, + "test.model.soft": `{"name":"soft","child":{"href":"/api/test/model"}}`, + "test.model.soft.parent": `{"name":"softparent","child":{"href":"/api/test/model/soft","model":{"name":"soft","child":{"href":"/api/test/model"}}}}`, + "test.model.data": `{"name":"data","primitive":12,"object":{"foo":["bar"]},"array":[{"foo":"bar"}]}`, + "test.model.data.parent": `{"name":"dataparent","child":{"href":"/api/test/model/data","model":{"name":"data","primitive":12,"object":{"foo":["bar"]},"array":[{"foo":"bar"}]}}}`, "test.m.a": `{"a":{"href":"/api/test/m/a"}}`, "test.m.b": `{"c":{"href":"/api/test/m/c","model":{"b":{"href":"/api/test/m/b"}}}}`, "test.m.d": `{"e":{"href":"/api/test/m/e","model":{"d":{"href":"/api/test/m/d"}}},"f":{"href":"/api/test/m/f","model":{"d":{"href":"/api/test/m/d"}}}}`, @@ -72,6 +76,10 @@ func TestHTTPGet(t *testing.T) { "test.collection.grandparent": `["grandparent",{"href":"/api/test/collection/parent","collection":["parent",{"href":"/api/test/collection","collection":` + resourceData("test.collection") + `}]}]`, "test.collection.secondparent": `["secondparent",{"href":"/api/test/collection","collection":` + resourceData("test.collection") + `}]`, "test.collection.brokenchild": `["brokenchild",{"href":"/api/test/err/notFound","error":` + resourceData("test.err.notFound") + `}]`, + "test.collection.soft": `["soft",{"href":"/api/test/collection"}]`, + "test.collection.soft.parent": `["softparent",{"href":"/api/test/collection/soft","collection":["soft",{"href":"/api/test/collection"}]}]`, + "test.collection.data": `["data",12,{"foo":["bar"]},[{"foo":"bar"}]]`, + "test.collection.data.parent": `["dataparent",{"href":"/api/test/collection/data","collection":["data",12,{"foo":["bar"]},[{"foo":"bar"}]]}]`, "test.c.a": `[{"href":"/api/test/c/a"}]`, "test.c.b": `[{"href":"/api/test/c/c","collection":[{"href":"/api/test/c/b"}]}]`, "test.c.d": `[{"href":"/api/test/c/e","collection":[{"href":"/api/test/c/d"}]},{"href":"/api/test/c/f","collection":[{"href":"/api/test/c/d"}]}]`, @@ -88,6 +96,10 @@ func TestHTTPGet(t *testing.T) { "test.model.grandparent": `{"name":"grandparent","child":{"name":"parent","child":` + resourceData("test.model") + `}}`, "test.model.secondparent": `{"name":"secondparent","child":` + resourceData("test.model") + `}`, "test.model.brokenchild": `{"name":"brokenchild","child":` + resourceData("test.err.notFound") + `}`, + "test.model.soft": `{"name":"soft","child":{"href":"/api/test/model"}}`, + "test.model.soft.parent": `{"name":"softparent","child":{"name":"soft","child":{"href":"/api/test/model"}}}`, + "test.model.data": `{"name":"data","primitive":12,"object":{"foo":["bar"]},"array":[{"foo":"bar"}]}`, + "test.model.data.parent": `{"name":"dataparent","child":{"name":"data","primitive":12,"object":{"foo":["bar"]},"array":[{"foo":"bar"}]}}`, "test.m.a": `{"a":{"href":"/api/test/m/a"}}`, "test.m.b": `{"c":{"b":{"href":"/api/test/m/b"}}}`, "test.m.d": `{"e":{"d":{"href":"/api/test/m/d"}},"f":{"d":{"href":"/api/test/m/d"}}}`, @@ -99,6 +111,10 @@ func TestHTTPGet(t *testing.T) { "test.collection.grandparent": `["grandparent",["parent",` + resourceData("test.collection") + `]]`, "test.collection.secondparent": `["secondparent",` + resourceData("test.collection") + `]`, "test.collection.brokenchild": `["brokenchild",` + resourceData("test.err.notFound") + `]`, + "test.collection.soft": `["soft",{"href":"/api/test/collection"}]`, + "test.collection.soft.parent": `["softparent",["soft",{"href":"/api/test/collection"}]]`, + "test.collection.data": `["data",12,{"foo":["bar"]},[{"foo":"bar"}]]`, + "test.collection.data.parent": `["dataparent",["data",12,{"foo":["bar"]},[{"foo":"bar"}]]]`, "test.c.a": `[{"href":"/api/test/c/a"}]`, "test.c.b": `[[{"href":"/api/test/c/b"}]]`, "test.c.d": `[[{"href":"/api/test/c/d"}],[{"href":"/api/test/c/d"}]]`, @@ -109,56 +125,61 @@ func TestHTTPGet(t *testing.T) { } for _, enc := range encodings { - for i, l := range sequenceTable { - runNamedTest(t, fmt.Sprintf("#%d with APIEncoding %#v", i+1, enc.APIEncoding), func(s *Session) { - var hreq *HTTPRequest - var req *Request + for _, set := range sequenceSets { + if set.Version != versionLatest { + continue + } + for i, l := range set.Table { + runNamedTest(t, fmt.Sprintf("#%d with APIEncoding %#v", i+1, enc.APIEncoding), func(s *Session) { + var hreq *HTTPRequest + var req *Request - hreqs := make(map[string]*HTTPRequest) - reqs := make(map[string]*Request) + hreqs := make(map[string]*HTTPRequest) + reqs := make(map[string]*Request) - for _, ev := range l { - switch ev.Event { - case "subscribe": - url := "/api/" + strings.Replace(ev.RID, ".", "/", -1) - hreqs[ev.RID] = s.HTTPRequest("GET", url, nil) - case "access": - for req = reqs["access."+ev.RID]; req == nil; req = reqs["access."+ev.RID] { - treq := s.GetRequest(t) - reqs[treq.Subject] = treq - } - req.RespondSuccess(json.RawMessage(`{"get":true}`)) - case "accessDenied": - for req = reqs["access."+ev.RID]; req == nil; req = reqs["access."+ev.RID] { - treq := s.GetRequest(t) - reqs[treq.Subject] = treq - } - req.RespondSuccess(json.RawMessage(`{"get":false}`)) - case "get": - for req = reqs["get."+ev.RID]; req == nil; req = reqs["get."+ev.RID] { - req = s.GetRequest(t) - reqs[req.Subject] = req - } - rsrc := resources[ev.RID] - switch rsrc.typ { - case typeModel: - req.RespondSuccess(json.RawMessage(`{"model":` + rsrc.data + `}`)) - case typeCollection: - req.RespondSuccess(json.RawMessage(`{"collection":` + rsrc.data + `}`)) - case typeError: - req.RespondError(rsrc.err) + for _, ev := range l { + switch ev.Event { + case "subscribe": + url := "/api/" + strings.Replace(ev.RID, ".", "/", -1) + hreqs[ev.RID] = s.HTTPRequest("GET", url, nil) + case "access": + for req = reqs["access."+ev.RID]; req == nil; req = reqs["access."+ev.RID] { + treq := s.GetRequest(t) + reqs[treq.Subject] = treq + } + req.RespondSuccess(json.RawMessage(`{"get":true}`)) + case "accessDenied": + for req = reqs["access."+ev.RID]; req == nil; req = reqs["access."+ev.RID] { + treq := s.GetRequest(t) + reqs[treq.Subject] = treq + } + req.RespondSuccess(json.RawMessage(`{"get":false}`)) + case "get": + for req = reqs["get."+ev.RID]; req == nil; req = reqs["get."+ev.RID] { + req = s.GetRequest(t) + reqs[req.Subject] = req + } + rsrc := resources[ev.RID] + switch rsrc.typ { + case typeModel: + req.RespondSuccess(json.RawMessage(`{"model":` + rsrc.data + `}`)) + case typeCollection: + req.RespondSuccess(json.RawMessage(`{"collection":` + rsrc.data + `}`)) + case typeError: + req.RespondError(rsrc.err) + } + case "response": + hreq = hreqs[ev.RID] + hreq.GetResponse(t).Equals(t, http.StatusOK, json.RawMessage(enc.Responses[ev.RID])) + case "errorResponse": + hreq = hreqs[ev.RID] + hreq.GetResponse(t).AssertIsError(t) } - case "response": - hreq = hreqs[ev.RID] - hreq.GetResponse(t).Equals(t, http.StatusOK, json.RawMessage(enc.Responses[ev.RID])) - case "errorResponse": - hreq = hreqs[ev.RID] - hreq.GetResponse(t).AssertIsError(t) } - } - }, func(c *server.Config) { - c.APIEncoding = enc.APIEncoding - }) + }, func(c *server.Config) { + c.APIEncoding = enc.APIEncoding + }) + } } } } diff --git a/test/20version_test.go b/test/20version_test.go index 5e747fb..cbc2ba5 100644 --- a/test/20version_test.go +++ b/test/20version_test.go @@ -5,14 +5,11 @@ import ( "fmt" "testing" - "github.com/resgateio/resgate/server" "github.com/resgateio/resgate/server/reserr" ) func TestVersion_Request_ReturnsExpectedResponse(t *testing.T) { - versionResult := json.RawMessage(fmt.Sprintf(`{"protocol":"%s"}`, server.ProtocolVersion)) - tbl := []struct { Params json.RawMessage Expected interface{} diff --git a/test/40legacy_1.2.0_test.go b/test/40legacy_1.2.0_test.go new file mode 100644 index 0000000..98a9813 --- /dev/null +++ b/test/40legacy_1.2.0_test.go @@ -0,0 +1,162 @@ +package test + +import ( + "encoding/json" + "fmt" + "testing" +) + +// Test change event effect on cached model +func TestLegacy120ChangeEvent_OnCachedModel(t *testing.T) { + tbl := []struct { + RID string // RID of resource to subscribe to + ChangeEvent string // Change event to send (raw JSON) + ExpectedChangeEvent string // Expected event sent to client (raw JSON. Empty means none) + ExpectedModel string // Expected model after event (raw JSON) + ExpectedErrors int + }{ + {"test.model", `{"values":{"soft":{"rid":"test.model.soft","soft":true}}}`, `{"values":{"soft":"test.model.soft"}}`, `{"string":"foo","int":42,"bool":true,"null":null,"soft":"test.model.soft"}`, 0}, + {"test.model.soft", `{"values":{"child":null}}`, `{"values":{"child":null}}`, `{"name":"soft","child":null}`, 0}, + {"test.model.soft", `{"values":{"child":{"action":"delete"}}}`, `{"values":{"child":{"action":"delete"}}}`, `{"name":"soft"}`, 0}, + + // Unchanged values + {"test.model.soft", `{"values":{"child":{"rid":"test.model","soft":true}}}`, "", `{"name":"soft","child":"test.model"}`, 0}, + } + + for i, l := range tbl { + for sameClient := true; sameClient; sameClient = false { + runNamedTest(t, fmt.Sprintf("#%d with the same client being %+v", i+1, sameClient), func(s *Session) { + var creq *ClientRequest + + rid := l.RID + c := s.ConnectWithVersion("1.2.0") + + // Subscribe to resource + r := resources[rid].data + // Send subscribe request + creq = c.Request("subscribe."+rid, nil) + // Handle model get and access request + mreqs := s.GetParallelRequests(t, 2) + req := mreqs.GetRequest(t, "access."+rid) + req.RespondSuccess(json.RawMessage(`{"get":true}`)) + mreqs.GetRequest(t, "get."+rid).RespondSuccess(json.RawMessage(`{"model":` + r + `}`)) + creq.GetResponse(t) + + // Send event on model and validate client event + s.ResourceEvent(rid, "change", json.RawMessage(l.ChangeEvent)) + if l.ExpectedChangeEvent == "" { + c.AssertNoEvent(t, rid+".change") + } else { + c.GetEvent(t).Equals(t, rid+".change", json.RawMessage(l.ExpectedChangeEvent)) + } + + if sameClient { + c.Request("unsubscribe."+rid, nil).GetResponse(t) + // Subscribe a second time + creq = c.Request("subscribe."+rid, nil) + } else { + c2 := s.Connect() + // Subscribe a second time + creq = c2.Request("subscribe."+rid, nil) + } + + // Handle model access request + s.GetRequest(t).AssertSubject(t, "access."+rid).RespondSuccess(json.RawMessage(`{"get":true}`)) + + // Validate client response + creq.GetResponse(t).AssertResult(t, json.RawMessage(`{"models":{"`+rid+`":`+l.ExpectedModel+`}}`)) + s.AssertErrorsLogged(t, l.ExpectedErrors) + }) + } + } +} + +// Test change event with new resource reference +func TestLegacy120ChangeEvent_WithSoftReferenceReplacedByResourceReference_SubscribesReference(t *testing.T) { + model := resourceData("test.model") + + runTest(t, func(s *Session) { + c := s.ConnectWithVersion("1.2.0") + // Subscribe to resource + rid := "test.model.soft" + r := resources[rid].data + // Send subscribe request + creq := c.Request("subscribe."+rid, nil) + // Handle model get and access request + mreqs := s.GetParallelRequests(t, 2) + req := mreqs.GetRequest(t, "access."+rid) + req.RespondSuccess(json.RawMessage(`{"get":true}`)) + mreqs.GetRequest(t, "get."+rid).RespondSuccess(json.RawMessage(`{"model":` + r + `}`)) + creq.GetResponse(t) + + // Send event on model and validate client event + s.ResourceEvent(rid, "change", json.RawMessage(`{"values":{"child":{"rid":"test.model","soft":false}}}`)) + + // Handle model get request + s. + GetRequest(t). + AssertSubject(t, "get.test.model"). + RespondSuccess(json.RawMessage(`{"model":` + model + `}`)) + + c.GetEvent(t).Equals(t, rid+".change", json.RawMessage(`{"values":{"child":{"rid":"test.model","soft":false}},"models":{"test.model":`+model+`}}`)) + + // Send event on model and validate client event + s.ResourceEvent("test.model", "custom", common.CustomEvent()) + c.GetEvent(t).Equals(t, "test.model.custom", common.CustomEvent()) + }) +} + +// Test add and remove event effects on cached collection +func TestLegacy120AddRemoveEvents_OnCachedCollection(t *testing.T) { + tbl := []struct { + RID string // Resource ID + EventName string // Name of the event. Either add or remove. + EventPayload string // Event payload (raw JSON) + ClientEventPayload string // Event payload as sent to client(raw JSON) + ExpectedCollection string // Expected collection after event (raw JSON) + }{ + {"test.collection", "add", `{"idx":0,"value":{"rid":"test.collection.soft","soft":true}}`, `{"idx":0,"value":"test.collection.soft"}`, `["test.collection.soft","foo",42,true,null]`}, + {"test.collection.soft", "remove", `{"idx":1}`, `{"idx":1}`, `["soft"]`}, + } + + for i, l := range tbl { + for sameClient := true; sameClient; sameClient = false { + runNamedTest(t, fmt.Sprintf("#%d with the same client being %+v", i+1, sameClient), func(s *Session) { + var creq *ClientRequest + + c := s.ConnectWithVersion("1.2.0") + // Subscribe to resource + rid := l.RID + r := resources[rid].data + // Send subscribe request + creq = c.Request("subscribe."+rid, nil) + // Handle model get and access request + mreqs := s.GetParallelRequests(t, 2) + req := mreqs.GetRequest(t, "access."+rid) + req.RespondSuccess(json.RawMessage(`{"get":true}`)) + mreqs.GetRequest(t, "get."+rid).RespondSuccess(json.RawMessage(`{"collection":` + r + `}`)) + creq.GetResponse(t) + + // Send event on collection and validate client event + s.ResourceEvent(rid, l.EventName, json.RawMessage(l.EventPayload)) + c.GetEvent(t).Equals(t, rid+"."+l.EventName, json.RawMessage(l.ClientEventPayload)) + + if sameClient { + c.Request("unsubscribe."+rid, nil).GetResponse(t) + // Subscribe a second time + creq = c.Request("subscribe."+rid, nil) + } else { + c2 := s.Connect() + // Subscribe a second time + creq = c2.Request("subscribe."+rid, nil) + } + + // Handle collection access request + s.GetRequest(t).AssertSubject(t, "access."+l.RID).RespondSuccess(json.RawMessage(`{"get":true}`)) + + // Validate client response + creq.GetResponse(t).AssertResult(t, json.RawMessage(`{"collections":{"`+l.RID+`":`+l.ExpectedCollection+`}}`)) + }) + } + } +} diff --git a/test/common.go b/test/common.go index 034002e..4f39857 100644 --- a/test/common.go +++ b/test/common.go @@ -14,20 +14,42 @@ func (c *commonData) CustomEvent() json.RawMessage { return json.RawMessage(`{"f // subscribeToTestModel makes a successful subscription to test.model // Returns the connection ID (cid) func subscribeToTestModel(t *testing.T, s *Session, c *Conn) string { - model := resourceData("test.model") + return subscribeToResource(t, s, c, "test.model") +} + +func subscribeToResource(t *testing.T, s *Session, c *Conn, rid string) string { + rsrc, ok := resources[rid] + if !ok { + panic("no resource named " + rid) + } + var r string + if rsrc.typ == typeError { + b, _ := json.Marshal(rsrc.err) + r = string(b) + } else { + r = rsrc.data + } // Send subscribe request - creq := c.Request("subscribe.test.model", nil) + creq := c.Request("subscribe."+rid, nil) // Handle model get and access request mreqs := s.GetParallelRequests(t, 2) - mreqs.GetRequest(t, "get.test.model").RespondSuccess(json.RawMessage(`{"model":` + model + `}`)) - req := mreqs.GetRequest(t, "access.test.model") + // Handle access + req := mreqs.GetRequest(t, "access."+rid) cid := req.PathPayload(t, "cid").(string) req.RespondSuccess(json.RawMessage(`{"get":true}`)) - - // Validate client response and validate - creq.GetResponse(t).AssertResult(t, json.RawMessage(`{"models":{"test.model":`+model+`}}`)) + // Handle resource and validate client response + switch rsrc.typ { + case typeModel: + mreqs.GetRequest(t, "get."+rid).RespondSuccess(json.RawMessage(`{"model":` + r + `}`)) + creq.GetResponse(t) // .AssertResult(t, json.RawMessage(`{"models":{"`+rid+`":`+r+`}}`)) + case typeCollection: + mreqs.GetRequest(t, "get."+rid).RespondSuccess(json.RawMessage(`{"collection":` + r + `}`)) + creq.GetResponse(t) // .AssertResult(t, json.RawMessage(`{"collections":{"`+rid+`":`+r+`}}`)) + default: + panic("invalid type") + } return cid } @@ -82,22 +104,7 @@ func subscribeToTestModelParentExt(t *testing.T, s *Session, c *Conn, childIsSub // subscribeToTestCollection makes a successful subscription to test.collection // Returns the connection ID (cid) of the access request func subscribeToTestCollection(t *testing.T, s *Session, c *Conn) string { - collection := resourceData("test.collection") - - // Send subscribe request - creq := c.Request("subscribe.test.collection", nil) - - // Handle collection get and access request - mreqs := s.GetParallelRequests(t, 2) - mreqs.GetRequest(t, "get.test.collection").RespondSuccess(json.RawMessage(`{"collection":` + collection + `}`)) - req := mreqs.GetRequest(t, "access.test.collection") - cid := req.PathPayload(t, "cid").(string) - req.RespondSuccess(json.RawMessage(`{"get":true}`)) - - // Validate client response and validate - creq.GetResponse(t).AssertResult(t, json.RawMessage(`{"collections":{"test.collection":`+collection+`}}`)) - - return cid + return subscribeToResource(t, s, c, "test.collection") } // subscribeToTestCollectionParent makes a successful subscription to test.collection.parent @@ -145,75 +152,68 @@ func getCID(t *testing.T, s *Session, c *Conn) string { // subscribeToTestQueryModel makes a successful subscription to test.model // with a query and the normalized query. Returns the connection ID (cid) func subscribeToTestQueryModel(t *testing.T, s *Session, c *Conn, q, normq string) string { - model := resourceData("test.model") + return subscribeToQueryResource(t, s, c, "test.model", q, normq) +} + +// subscribeToTestQueryCollection makes a successful subscription to test.collection +// with a query and the normalized query. Returns the connection ID (cid) +func subscribeToTestQueryCollection(t *testing.T, s *Session, c *Conn, q, normq string) string { + return subscribeToQueryResource(t, s, c, "test.collection", q, normq) +} + +func subscribeToQueryResource(t *testing.T, s *Session, c *Conn, rid, q, normq string) string { + rsrc, ok := resources[rid] + if !ok { + panic("no resource named " + rid) + } + var r string + if rsrc.typ == typeError { + b, _ := json.Marshal(rsrc.err) + r = string(b) + } else { + r = rsrc.data + } normqj, err := json.Marshal(normq) if err != nil { panic("test: failed to marshal normalized query: " + err.Error()) } - rid := "test.model" + fullrid := rid if q != "" { - rid += "?" + q + fullrid += "?" + q } - qj, err := json.Marshal(rid) + qj, err := json.Marshal(fullrid) if err != nil { panic("test: failed to marshal query: " + err.Error()) } // Send subscribe request - creq := c.Request("subscribe."+rid, nil) + creq := c.Request("subscribe."+fullrid, nil) - // Handle model get and access request + // Handle resource get and access request mreqs := s.GetParallelRequests(t, 2) - req := mreqs.GetRequest(t, "get.test.model") - if q != "" { - req.AssertPathPayload(t, "query", q) - } - req.RespondSuccess(json.RawMessage(`{"model":` + model + `,"query":` + string(normqj) + `}`)) - req = mreqs.GetRequest(t, "access.test.model") + // Handle access request + req := mreqs.GetRequest(t, "access."+rid) if q != "" { req.AssertPathPayload(t, "query", q) } cid := req.PathPayload(t, "cid").(string) req.RespondSuccess(json.RawMessage(`{"get":true}`)) - - // Validate client response and validate - creq.GetResponse(t).AssertResult(t, json.RawMessage(`{"models":{`+string(qj)+`:`+model+`}}`)) - - return cid -} - -// subscribeToTestQueryCollection makes a successful subscription to test.collection -// with a query and the normalized query. Returns the connection ID (cid) -func subscribeToTestQueryCollection(t *testing.T, s *Session, c *Conn, q, normq string) string { - collection := resourceData("test.collection") - - normqj, err := json.Marshal(normq) - if err != nil { - panic("test: failed to marshal normalized query: " + err.Error()) + // Handle resource and validate client response + req = mreqs.GetRequest(t, "get."+rid) + if q != "" { + req.AssertPathPayload(t, "query", q) } - - qj, err := json.Marshal("test.collection?" + q) - if err != nil { - panic("test: failed to marshal query: " + err.Error()) + switch rsrc.typ { + case typeModel: + req.RespondSuccess(json.RawMessage(`{"model":` + r + `,"query":` + string(normqj) + `}`)) + creq.GetResponse(t).AssertResult(t, json.RawMessage(`{"models":{`+string(qj)+`:`+r+`}}`)) + case typeCollection: + req.RespondSuccess(json.RawMessage(`{"collection":` + r + `,"query":` + string(normqj) + `}`)) + creq.GetResponse(t).AssertResult(t, json.RawMessage(`{"collections":{`+string(qj)+`:`+r+`}}`)) + default: + panic("invalid type") } - - // Send subscribe request - creq := c.Request("subscribe.test.collection?"+q, nil) - - // Handle collection get and access request - mreqs := s.GetParallelRequests(t, 2) - mreqs. - GetRequest(t, "get.test.collection"). - AssertPathPayload(t, "query", q). - RespondSuccess(json.RawMessage(`{"collection":` + collection + `,"query":` + string(normqj) + `}`)) - req := mreqs.GetRequest(t, "access.test.collection").AssertPathPayload(t, "query", q) - cid := req.PathPayload(t, "cid").(string) - req.RespondSuccess(json.RawMessage(`{"get":true}`)) - - // Validate client response and validate - creq.GetResponse(t).AssertResult(t, json.RawMessage(`{"collections":{`+string(qj)+`:`+collection+`}}`)) - return cid } diff --git a/test/natstest.go b/test/natstest.go index 8ae5021..760e225 100644 --- a/test/natstest.go +++ b/test/natstest.go @@ -182,6 +182,19 @@ func (c *NATSTestClient) HasSubscriptions(t *testing.T, rids ...string) { } } +// NoSubscriptions asserts that there isn't any subscription for the given +// resource IDs. +func (c *NATSTestClient) NoSubscriptions(t *testing.T, rids ...string) { + c.mu.Lock() + defer c.mu.Unlock() + + for _, rid := range rids { + if _, ok := c.subs["event."+rid]; ok { + t.Fatalf("expected no subscription for event.%s.*, but found one", rid) + } + } +} + // ResourceEvent sends a resource event to resgate. The subject will be "event."+rid+"."+event . // It panics if there is no subscription for such event. func (c *NATSTestClient) ResourceEvent(rid string, event string, payload interface{}) { diff --git a/test/resources.go b/test/resources.go index 734405e..0ee3816 100644 --- a/test/resources.go +++ b/test/resources.go @@ -34,7 +34,10 @@ type resource struct { } func resourceData(rid string) string { - rsrc := resources[rid] + rsrc, ok := resources[rid] + if !ok { + panic("no resource named " + rid) + } if rsrc.typ == typeError { b, _ := json.Marshal(rsrc.err) return string(b) @@ -49,6 +52,10 @@ var resources = map[string]resource{ "test.model.secondparent": {typeModel, `{"name":"secondparent","child":{"rid":"test.model"}}`, nil}, "test.model.grandparent": {typeModel, `{"name":"grandparent","child":{"rid":"test.model.parent"}}`, nil}, "test.model.brokenchild": {typeModel, `{"name":"brokenchild","child":{"rid":"test.err.notFound"}}`, nil}, + "test.model.soft": {typeModel, `{"name":"soft","child":{"rid":"test.model","soft":true}}`, nil}, + "test.model.soft.parent": {typeModel, `{"name":"softparent","child":{"rid":"test.model.soft","soft":false}}`, nil}, + "test.model.data": {typeModel, `{"name":"data","primitive":{"data":12},"object":{"data":{"foo":["bar"]}},"array":{"data":[{"foo":"bar"}]}}`, nil}, + "test.model.data.parent": {typeModel, `{"name":"dataparent","child":{"rid":"test.model.data"}}`, nil}, // Cyclic model resources "test.m.a": {typeModel, `{"a":{"rid":"test.m.a"}}`, nil}, @@ -69,6 +76,10 @@ var resources = map[string]resource{ "test.collection.secondparent": {typeCollection, `["secondparent",{"rid":"test.collection"}]`, nil}, "test.collection.grandparent": {typeCollection, `["grandparent",{"rid":"test.collection.parent"}]`, nil}, "test.collection.brokenchild": {typeCollection, `["brokenchild",{"rid":"test.err.notFound"}]`, nil}, + "test.collection.soft": {typeCollection, `["soft",{"rid":"test.collection","soft":true}]`, nil}, + "test.collection.soft.parent": {typeCollection, `["softparent",{"rid":"test.collection.soft","soft":false}]`, nil}, + "test.collection.data": {typeCollection, `["data",{"data":12},{"data":{"foo":["bar"]}},{"data":[{"foo":"bar"}]}]`, nil}, + "test.collection.data.parent": {typeCollection, `["dataparent",{"rid":"test.collection.data"}]`, nil}, // Cyclic collection resources "test.c.a": {typeCollection, `[{"rid":"test.c.a"}]`, nil}, @@ -100,213 +111,367 @@ type sequenceEvent struct { RID string } -var sequenceTable = [][]sequenceEvent{ - // Model tests - { - {"subscribe", "test.model"}, - {"access", "test.model"}, - {"get", "test.model"}, - {"response", "test.model"}, - {"event", "test.model"}, - }, - { - {"subscribe", "test.model.parent"}, - {"access", "test.model.parent"}, - {"get", "test.model.parent"}, - {"get", "test.model"}, - {"response", "test.model.parent"}, - {"event", "test.model.parent"}, - {"event", "test.model"}, - }, - { - {"subscribe", "test.model.grandparent"}, - {"access", "test.model.grandparent"}, - {"get", "test.model.grandparent"}, - {"get", "test.model.parent"}, - {"get", "test.model"}, - {"response", "test.model.grandparent"}, - {"event", "test.model.grandparent"}, - {"event", "test.model.parent"}, - {"event", "test.model"}, - }, - { - {"subscribe", "test.model.parent"}, - {"access", "test.model.parent"}, - {"get", "test.model.parent"}, - {"get", "test.model"}, - {"response", "test.model.parent"}, - {"subscribe", "test.model.secondparent"}, - {"access", "test.model.secondparent"}, - {"get", "test.model.secondparent"}, - {"response", "test.model.secondparent"}, - }, - { - {"subscribe", "test.model.brokenchild"}, - {"access", "test.model.brokenchild"}, - {"get", "test.model.brokenchild"}, - {"get", "test.err.notFound"}, - {"response", "test.model.brokenchild"}, - {"event", "test.model.brokenchild"}, - {"noevent", "test.err.notFound"}, - }, - // Cyclic model tests - { - {"subscribe", "test.m.a"}, - {"access", "test.m.a"}, - {"get", "test.m.a"}, - {"response", "test.m.a"}, - }, - { - {"subscribe", "test.m.b"}, - {"access", "test.m.b"}, - {"get", "test.m.b"}, - {"get", "test.m.c"}, - {"response", "test.m.b"}, - }, - { - {"subscribe", "test.m.d"}, - {"access", "test.m.d"}, - {"get", "test.m.d"}, - {"get", "test.m.e"}, - {"get", "test.m.f"}, - {"response", "test.m.d"}, - }, - { - {"subscribe", "test.m.g"}, - {"access", "test.m.g"}, - {"get", "test.m.g"}, - {"get", "test.m.e"}, - {"get", "test.m.f"}, - {"get", "test.m.d"}, - {"response", "test.m.g"}, - }, - { - {"subscribe", "test.m.d"}, - {"access", "test.m.d"}, - {"get", "test.m.d"}, - {"subscribe", "test.m.h"}, - {"access", "test.m.h"}, - {"get", "test.m.e"}, - {"get", "test.m.h"}, - {"get", "test.m.f"}, - {"response", "test.m.d"}, - {"response", "test.m.h"}, - }, +type sequenceSet struct { + Version string + Table [][]sequenceEvent +} - // Collection tests +var sequenceSets = []sequenceSet{ { - {"subscribe", "test.collection"}, - {"access", "test.collection"}, - {"get", "test.collection"}, - {"response", "test.collection"}, - {"event", "test.collection"}, - }, - { - {"subscribe", "test.collection.parent"}, - {"access", "test.collection.parent"}, - {"get", "test.collection.parent"}, - {"get", "test.collection"}, - {"response", "test.collection.parent"}, - {"event", "test.collection.parent"}, - {"event", "test.collection"}, - }, - { - {"subscribe", "test.collection.grandparent"}, - {"access", "test.collection.grandparent"}, - {"get", "test.collection.grandparent"}, - {"get", "test.collection.parent"}, - {"get", "test.collection"}, - {"response", "test.collection.grandparent"}, - {"event", "test.collection.grandparent"}, - {"event", "test.collection.parent"}, - {"event", "test.collection"}, - }, - { - {"subscribe", "test.collection.parent"}, - {"access", "test.collection.parent"}, - {"get", "test.collection.parent"}, - {"get", "test.collection"}, - {"response", "test.collection.parent"}, - {"subscribe", "test.collection.secondparent"}, - {"access", "test.collection.secondparent"}, - {"get", "test.collection.secondparent"}, - {"response", "test.collection.secondparent"}, - }, - { - {"subscribe", "test.collection.brokenchild"}, - {"access", "test.collection.brokenchild"}, - {"get", "test.collection.brokenchild"}, - {"get", "test.err.notFound"}, - {"response", "test.collection.brokenchild"}, - {"event", "test.collection.brokenchild"}, - {"noevent", "test.err.notFound"}, - }, - // Cyclic collection tests - { - {"subscribe", "test.c.a"}, - {"access", "test.c.a"}, - {"get", "test.c.a"}, - {"response", "test.c.a"}, - }, - { - {"subscribe", "test.c.b"}, - {"access", "test.c.b"}, - {"get", "test.c.b"}, - {"get", "test.c.c"}, - {"response", "test.c.b"}, - }, - { - {"subscribe", "test.c.d"}, - {"access", "test.c.d"}, - {"get", "test.c.d"}, - {"get", "test.c.e"}, - {"get", "test.c.f"}, - {"response", "test.c.d"}, - }, - { - {"subscribe", "test.c.g"}, - {"access", "test.c.g"}, - {"get", "test.c.g"}, - {"get", "test.c.e"}, - {"get", "test.c.f"}, - {"get", "test.c.d"}, - {"response", "test.c.g"}, - }, - { - {"subscribe", "test.c.d"}, - {"access", "test.c.d"}, - {"get", "test.c.d"}, - {"subscribe", "test.c.h"}, - {"access", "test.c.h"}, - {"get", "test.c.e"}, - {"get", "test.c.h"}, - {"get", "test.c.f"}, - {"response", "test.c.d"}, - {"response", "test.c.h"}, - }, - // Access test - { - {"subscribe", "test.model.parent"}, - {"access", "test.model.parent"}, - {"get", "test.model.parent"}, - {"get", "test.model"}, - {"response", "test.model.parent"}, - {"subscribe", "test.model"}, - {"access", "test.model"}, - {"response", "test.model"}, - {"event", "test.model.parent"}, - {"event", "test.model"}, + versionLatest, + [][]sequenceEvent{ + // Model tests + { + {"subscribe", "test.model"}, + {"access", "test.model"}, + {"get", "test.model"}, + {"response", "test.model"}, + {"event", "test.model"}, + }, + { + {"subscribe", "test.model.parent"}, + {"access", "test.model.parent"}, + {"get", "test.model.parent"}, + {"get", "test.model"}, + {"response", "test.model.parent"}, + {"event", "test.model.parent"}, + {"event", "test.model"}, + }, + { + {"subscribe", "test.model.grandparent"}, + {"access", "test.model.grandparent"}, + {"get", "test.model.grandparent"}, + {"get", "test.model.parent"}, + {"get", "test.model"}, + {"response", "test.model.grandparent"}, + {"event", "test.model.grandparent"}, + {"event", "test.model.parent"}, + {"event", "test.model"}, + }, + { + {"subscribe", "test.model.parent"}, + {"access", "test.model.parent"}, + {"get", "test.model.parent"}, + {"get", "test.model"}, + {"response", "test.model.parent"}, + {"subscribe", "test.model.secondparent"}, + {"access", "test.model.secondparent"}, + {"get", "test.model.secondparent"}, + {"response", "test.model.secondparent"}, + }, + { + {"subscribe", "test.model.brokenchild"}, + {"access", "test.model.brokenchild"}, + {"get", "test.model.brokenchild"}, + {"get", "test.err.notFound"}, + {"response", "test.model.brokenchild"}, + {"event", "test.model.brokenchild"}, + {"noevent", "test.err.notFound"}, + }, + { + {"subscribe", "test.model.soft"}, + {"access", "test.model.soft"}, + {"get", "test.model.soft"}, + {"response", "test.model.soft"}, + {"event", "test.model.soft"}, + {"nosubscription", "test.model"}, + }, + { + {"subscribe", "test.model.soft.parent"}, + {"access", "test.model.soft.parent"}, + {"get", "test.model.soft.parent"}, + {"get", "test.model.soft"}, + {"response", "test.model.soft.parent"}, + {"event", "test.model.soft.parent"}, + {"event", "test.model.soft"}, + {"nosubscription", "test.model"}, + }, + { + {"subscribe", "test.model.data"}, + {"access", "test.model.data"}, + {"get", "test.model.data"}, + {"response", "test.model.data"}, + {"event", "test.model.data"}, + }, + { + {"subscribe", "test.model.data.parent"}, + {"access", "test.model.data.parent"}, + {"get", "test.model.data.parent"}, + {"get", "test.model.data"}, + {"response", "test.model.data.parent"}, + {"event", "test.model.data.parent"}, + {"event", "test.model.data"}, + }, + // Cyclic model tests + { + {"subscribe", "test.m.a"}, + {"access", "test.m.a"}, + {"get", "test.m.a"}, + {"response", "test.m.a"}, + }, + { + {"subscribe", "test.m.b"}, + {"access", "test.m.b"}, + {"get", "test.m.b"}, + {"get", "test.m.c"}, + {"response", "test.m.b"}, + }, + { + {"subscribe", "test.m.d"}, + {"access", "test.m.d"}, + {"get", "test.m.d"}, + {"get", "test.m.e"}, + {"get", "test.m.f"}, + {"response", "test.m.d"}, + }, + { + {"subscribe", "test.m.g"}, + {"access", "test.m.g"}, + {"get", "test.m.g"}, + {"get", "test.m.e"}, + {"get", "test.m.f"}, + {"get", "test.m.d"}, + {"response", "test.m.g"}, + }, + { + {"subscribe", "test.m.d"}, + {"access", "test.m.d"}, + {"get", "test.m.d"}, + {"subscribe", "test.m.h"}, + {"access", "test.m.h"}, + {"get", "test.m.e"}, + {"get", "test.m.h"}, + {"get", "test.m.f"}, + {"response", "test.m.d"}, + {"response", "test.m.h"}, + }, + + // Collection tests + { + {"subscribe", "test.collection"}, + {"access", "test.collection"}, + {"get", "test.collection"}, + {"response", "test.collection"}, + {"event", "test.collection"}, + }, + { + {"subscribe", "test.collection.parent"}, + {"access", "test.collection.parent"}, + {"get", "test.collection.parent"}, + {"get", "test.collection"}, + {"response", "test.collection.parent"}, + {"event", "test.collection.parent"}, + {"event", "test.collection"}, + }, + { + {"subscribe", "test.collection.grandparent"}, + {"access", "test.collection.grandparent"}, + {"get", "test.collection.grandparent"}, + {"get", "test.collection.parent"}, + {"get", "test.collection"}, + {"response", "test.collection.grandparent"}, + {"event", "test.collection.grandparent"}, + {"event", "test.collection.parent"}, + {"event", "test.collection"}, + }, + { + {"subscribe", "test.collection.parent"}, + {"access", "test.collection.parent"}, + {"get", "test.collection.parent"}, + {"get", "test.collection"}, + {"response", "test.collection.parent"}, + {"subscribe", "test.collection.secondparent"}, + {"access", "test.collection.secondparent"}, + {"get", "test.collection.secondparent"}, + {"response", "test.collection.secondparent"}, + }, + { + {"subscribe", "test.collection.brokenchild"}, + {"access", "test.collection.brokenchild"}, + {"get", "test.collection.brokenchild"}, + {"get", "test.err.notFound"}, + {"response", "test.collection.brokenchild"}, + {"event", "test.collection.brokenchild"}, + {"noevent", "test.err.notFound"}, + }, + { + {"subscribe", "test.collection.soft"}, + {"access", "test.collection.soft"}, + {"get", "test.collection.soft"}, + {"response", "test.collection.soft"}, + {"event", "test.collection.soft"}, + {"nosubscription", "test.collection"}, + }, + { + {"subscribe", "test.collection.soft.parent"}, + {"access", "test.collection.soft.parent"}, + {"get", "test.collection.soft.parent"}, + {"get", "test.collection.soft"}, + {"response", "test.collection.soft.parent"}, + {"event", "test.collection.soft.parent"}, + {"event", "test.collection.soft"}, + {"nosubscription", "test.collection"}, + }, + { + {"subscribe", "test.collection.data"}, + {"access", "test.collection.data"}, + {"get", "test.collection.data"}, + {"response", "test.collection.data"}, + {"event", "test.collection.data"}, + {"nosubscription", "test.collection"}, + }, + { + {"subscribe", "test.collection.data.parent"}, + {"access", "test.collection.data.parent"}, + {"get", "test.collection.data.parent"}, + {"get", "test.collection.data"}, + {"response", "test.collection.data.parent"}, + {"event", "test.collection.data.parent"}, + {"event", "test.collection.data"}, + }, + // Cyclic collection tests + { + {"subscribe", "test.c.a"}, + {"access", "test.c.a"}, + {"get", "test.c.a"}, + {"response", "test.c.a"}, + }, + { + {"subscribe", "test.c.b"}, + {"access", "test.c.b"}, + {"get", "test.c.b"}, + {"get", "test.c.c"}, + {"response", "test.c.b"}, + }, + { + {"subscribe", "test.c.d"}, + {"access", "test.c.d"}, + {"get", "test.c.d"}, + {"get", "test.c.e"}, + {"get", "test.c.f"}, + {"response", "test.c.d"}, + }, + { + {"subscribe", "test.c.g"}, + {"access", "test.c.g"}, + {"get", "test.c.g"}, + {"get", "test.c.e"}, + {"get", "test.c.f"}, + {"get", "test.c.d"}, + {"response", "test.c.g"}, + }, + { + {"subscribe", "test.c.d"}, + {"access", "test.c.d"}, + {"get", "test.c.d"}, + {"subscribe", "test.c.h"}, + {"access", "test.c.h"}, + {"get", "test.c.e"}, + {"get", "test.c.h"}, + {"get", "test.c.f"}, + {"response", "test.c.d"}, + {"response", "test.c.h"}, + }, + // Access test + { + {"subscribe", "test.model.parent"}, + {"access", "test.model.parent"}, + {"get", "test.model.parent"}, + {"get", "test.model"}, + {"response", "test.model.parent"}, + {"subscribe", "test.model"}, + {"access", "test.model"}, + {"response", "test.model"}, + {"event", "test.model.parent"}, + {"event", "test.model"}, + }, + { + {"subscribe", "test.model.parent"}, + {"access", "test.model.parent"}, + {"get", "test.model.parent"}, + {"get", "test.model"}, + {"response", "test.model.parent"}, + {"subscribe", "test.model"}, + {"accessDenied", "test.model"}, + {"errorResponse", "test.model"}, + {"event", "test.model.parent"}, + {"event", "test.model"}, + }, + }, }, { - {"subscribe", "test.model.parent"}, - {"access", "test.model.parent"}, - {"get", "test.model.parent"}, - {"get", "test.model"}, - {"response", "test.model.parent"}, - {"subscribe", "test.model"}, - {"accessDenied", "test.model"}, - {"errorResponse", "test.model"}, - {"event", "test.model.parent"}, - {"event", "test.model"}, + "1.2.0", + [][]sequenceEvent{ + // Model tests + { + {"subscribe", "test.model.soft"}, + {"access", "test.model.soft"}, + {"get", "test.model.soft"}, + {"response", "test.model.soft"}, + {"event", "test.model.soft"}, + {"nosubscription", "test.model"}, + }, + { + {"subscribe", "test.model.soft.parent"}, + {"access", "test.model.soft.parent"}, + {"get", "test.model.soft.parent"}, + {"get", "test.model.soft"}, + {"response", "test.model.soft.parent"}, + {"event", "test.model.soft.parent"}, + {"event", "test.model.soft"}, + }, + { + {"subscribe", "test.model.data"}, + {"access", "test.model.data"}, + {"get", "test.model.data"}, + {"response", "test.model.data"}, + {"event", "test.model.data"}, + {"nosubscription", "test.model"}, + }, + { + {"subscribe", "test.model.data.parent"}, + {"access", "test.model.data.parent"}, + {"get", "test.model.data.parent"}, + {"get", "test.model.data"}, + {"response", "test.model.data.parent"}, + {"event", "test.model.data.parent"}, + {"event", "test.model.data"}, + }, + // Collection tests + { + {"subscribe", "test.collection.soft"}, + {"access", "test.collection.soft"}, + {"get", "test.collection.soft"}, + {"response", "test.collection.soft"}, + {"event", "test.collection.soft"}, + {"nosubscription", "test.collection"}, + }, + { + {"subscribe", "test.collection.soft.parent"}, + {"access", "test.collection.soft.parent"}, + {"get", "test.collection.soft.parent"}, + {"get", "test.collection.soft"}, + {"response", "test.collection.soft.parent"}, + {"event", "test.collection.soft.parent"}, + {"event", "test.collection.soft"}, + }, + { + {"subscribe", "test.collection.data"}, + {"access", "test.collection.data"}, + {"get", "test.collection.data"}, + {"response", "test.collection.data"}, + {"event", "test.collection.data"}, + {"nosubscription", "test.collection"}, + }, + { + {"subscribe", "test.collection.data.parent"}, + {"access", "test.collection.data.parent"}, + {"get", "test.collection.data.parent"}, + {"get", "test.collection.data"}, + {"response", "test.collection.data.parent"}, + {"event", "test.collection.data.parent"}, + {"event", "test.collection.data"}, + }, + }, }, } diff --git a/test/test.go b/test/test.go index ee3a760..e20a600 100644 --- a/test/test.go +++ b/test/test.go @@ -16,7 +16,8 @@ import ( const timeoutSeconds = 1 var ( - versionRequest = json.RawMessage(`{"protocol":"1.999.999"}`) + versionLatest = "1.999.999" + versionRequest = json.RawMessage(fmt.Sprintf(`{"protocol":"%s"}`, versionLatest)) versionResult = json.RawMessage(fmt.Sprintf(`{"protocol":"%s"}`, server.ProtocolVersion)) ) @@ -84,6 +85,20 @@ func (s *Session) Connect() *Conn { return c } +// ConnectWithVersion makes a new mock client websocket connection +// that handshakes with the version string provided. +func (s *Session) ConnectWithVersion(version string) *Conn { + c := s.connect(make(chan *ClientEvent, 256), nil) + + // Send version connect + creq := c.Request("version", struct { + Protocol string `json:"protocol"` + }{version}) + cresp := creq.GetResponse(s.t) + cresp.AssertResult(s.t, versionResult) + return c +} + // ConnectWithoutVersion makes a new mock client websocket connection // without any version handshake. func (s *Session) ConnectWithoutVersion() *Conn {