Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,674 changes: 2,583 additions & 91 deletions dist/web/pubnub.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions dist/web/pubnub.min.js

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions dist/web/pubnub.worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -1861,6 +1861,10 @@
* Request will be sent using `PATCH` method.
*/
TransportMethod["PATCH"] = "PATCH";
/**
* Request will be sent using `PUT` method.
*/
TransportMethod["PUT"] = "PUT";
/**
* Request will be sent using `DELETE` method.
*/
Expand Down
2 changes: 1 addition & 1 deletion dist/web/pubnub.worker.min.js

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions lib/core/components/event-dispatcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,15 @@ class EventDispatcher {
set onFile(listener) {
this.updateTypeOrObjectListener({ add: !!listener, listener, type: 'file' });
}
/**
* Set a new DataSync event handler.
*
* @param listener - Listener function, which will be called each time when a new
* DataSync event is received from the real-time network.
*/
set onDataSync(listener) {
this.updateTypeOrObjectListener({ add: !!listener, listener, type: 'dataSync' });
}
/**
* Dispatch received a real-time update.
*
Expand Down Expand Up @@ -140,6 +149,8 @@ class EventDispatcher {
this.announce('messageAction', event.data);
else if (event.type === subscribe_1.PubNubEventType.Files)
this.announce('file', event.data);
else if (event.type === subscribe_1.PubNubEventType.DataSync)
this.announce('dataSync', event.data);
}
/**
* Dispatch received connection status change.
Expand Down
4 changes: 3 additions & 1 deletion lib/core/components/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,9 @@ class AbstractRequest {
if (headers)
request.headers = headers;
// Attach body (if required).
if (request.method === transport_request_1.TransportMethod.POST || request.method === transport_request_1.TransportMethod.PATCH) {
if (request.method === transport_request_1.TransportMethod.POST ||
request.method === transport_request_1.TransportMethod.PATCH ||
request.method === transport_request_1.TransportMethod.PUT) {
const [body, formData] = [this.body, this.formData];
if (formData)
request.formData = formData;
Expand Down
123 changes: 123 additions & 0 deletions lib/core/constants/operations.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,129 @@ var RequestOperation;
*/
RequestOperation["PNSetMembershipsOperation"] = "PNSetMembershipsOperation";
// --------------------------------------------------------
// ------------------- DataSync API ----------------------
// --------------------------------------------------------
/**
* Create entity REST API operation.
*/
RequestOperation["PNCreateEntityOperation"] = "PNCreateEntityOperation";
/**
* Get entity REST API operation.
*/
RequestOperation["PNGetEntityOperation"] = "PNGetEntityOperation";
/**
* Get all entities REST API operation.
*/
RequestOperation["PNGetAllEntitiesOperation"] = "PNGetAllEntitiesOperation";
/**
* Update entity REST API operation.
*/
RequestOperation["PNUpdateEntityOperation"] = "PNUpdateEntityOperation";
/**
* Patch entity REST API operation.
*/
RequestOperation["PNPatchEntityOperation"] = "PNPatchEntityOperation";
/**
* Remove entity REST API operation.
*/
RequestOperation["PNRemoveEntityOperation"] = "PNRemoveEntityOperation";
/**
* Create relationship REST API operation.
*/
RequestOperation["PNCreateRelationshipOperation"] = "PNCreateRelationshipOperation";
/**
* Get relationship REST API operation.
*/
RequestOperation["PNGetRelationshipOperation"] = "PNGetRelationshipOperation";
/**
* Get all relationships REST API operation.
*/
RequestOperation["PNGetAllRelationshipsOperation"] = "PNGetAllRelationshipsOperation";
/**
* Update relationship REST API operation.
*/
RequestOperation["PNUpdateRelationshipOperation"] = "PNUpdateRelationshipOperation";
/**
* Patch relationship REST API operation.
*/
RequestOperation["PNPatchRelationshipOperation"] = "PNPatchRelationshipOperation";
/**
* Remove relationship REST API operation.
*/
RequestOperation["PNRemoveRelationshipOperation"] = "PNRemoveRelationshipOperation";
/**
* Create user REST API operation.
*/
RequestOperation["PNCreateUserOperation"] = "PNCreateUserOperation";
/**
* Get user REST API operation.
*/
RequestOperation["PNGetUserOperation"] = "PNGetUserOperation";
/**
* Get all users REST API operation.
*/
RequestOperation["PNGetAllUsersOperation"] = "PNGetAllUsersOperation";
/**
* Update user REST API operation.
*/
RequestOperation["PNUpdateUserOperation"] = "PNUpdateUserOperation";
/**
* Patch user REST API operation.
*/
RequestOperation["PNPatchUserOperation"] = "PNPatchUserOperation";
/**
* Remove user REST API operation.
*/
RequestOperation["PNRemoveUserOperation"] = "PNRemoveUserOperation";
/**
* Create channel REST API operation.
*/
RequestOperation["PNCreateChannelOperation"] = "PNCreateChannelOperation";
/**
* Get channel REST API operation.
*/
RequestOperation["PNGetChannelOperation"] = "PNGetChannelOperation";
/**
* Get all channels REST API operation.
*/
RequestOperation["PNGetAllChannelsOperation"] = "PNGetAllChannelsOperation";
/**
* Update channel REST API operation.
*/
RequestOperation["PNUpdateChannelOperation"] = "PNUpdateChannelOperation";
/**
* Patch channel REST API operation.
*/
RequestOperation["PNPatchChannelOperation"] = "PNPatchChannelOperation";
/**
* Remove channel REST API operation.
*/
RequestOperation["PNRemoveChannelOperation"] = "PNRemoveChannelOperation";
/**
* Create membership REST API operation.
*/
RequestOperation["PNCreateMembershipOperation"] = "PNCreateMembershipOperation";
/**
* Get membership REST API operation.
*/
RequestOperation["PNGetMembershipOperation"] = "PNGetMembershipOperation";
/**
* Get all memberships REST API operation.
*/
RequestOperation["PNGetAllMembershipsOperation"] = "PNGetAllMembershipsOperation";
/**
* Update membership REST API operation.
*/
RequestOperation["PNUpdateMembershipOperation"] = "PNUpdateMembershipOperation";
/**
* Patch membership REST API operation.
*/
RequestOperation["PNPatchMembershipOperation"] = "PNPatchMembershipOperation";
/**
* Remove membership REST API operation.
*/
RequestOperation["PNRemoveMembershipOperation"] = "PNRemoveMembershipOperation";
// --------------------------------------------------------
// -------------------- File Upload API -------------------
// --------------------------------------------------------
/**
Expand Down
73 changes: 70 additions & 3 deletions lib/core/endpoints/access_manager/grant_token.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,16 @@ class GrantTokenRequest extends request_1.AbstractRequest {
validate() {
var _a, _b, _c, _d, _e, _f;
const { keySet: { subscribeKey, publishKey, secretKey }, resources, patterns, } = this.parameters;
// DataSync projections are a standalone grant target — a request carrying only projections
// (no resources / patterns permissions) is still valid.
const hasProjections = this.buildProjections() !== undefined;
if (!subscribeKey)
return 'Missing Subscribe Key';
if (!publishKey)
return 'Missing Publish Key';
if (!secretKey)
return 'Missing Secret Key';
if (!resources && !patterns)
if (!resources && !patterns && !hasProjections)
return 'Missing either Resources or Patterns';
if (this.isVspPermissions(this.parameters) &&
('channels' in ((_a = this.parameters.resources) !== null && _a !== void 0 ? _a : {}) ||
Expand All @@ -70,7 +73,7 @@ class GrantTokenRequest extends request_1.AbstractRequest {
}
});
});
if (permissionsEmpty)
if (permissionsEmpty && !hasProjections)
return 'Missing values for either Resources or Patterns';
}
parse(response) {
Expand Down Expand Up @@ -133,15 +136,77 @@ class GrantTokenRequest extends request_1.AbstractRequest {
Object.keys(channelsPermissions).forEach((channel) => mapPermissions(channel, this.extractPermissions(channelsPermissions[channel]), 'channels', target));
Object.keys(channelGroupsPermissions).forEach((groups) => mapPermissions(groups, this.extractPermissions(channelGroupsPermissions[groups]), 'groups', target));
Object.keys(uuidsPermissions).forEach((uuids) => mapPermissions(uuids, this.extractPermissions(uuidsPermissions[uuids]), 'uuids', target));
if (refPerm && 'dataSync' in refPerm)
this.mapDataSyncPermissions(refPerm.dataSync, target, mapPermissions);
});
if (uuid)
permissions.uuid = `${uuid}`;
permissions.resources = resourcePermissions;
permissions.patterns = patternPermissions;
permissions.meta = meta !== null && meta !== void 0 ? meta : {};
// Merge DataSync projections into `meta` under `pn-projections`, preserving user-supplied meta.
// `pn-projections` is omitted entirely when no projections are set.
const projections = this.buildProjections();
permissions.meta = Object.assign(Object.assign({}, (meta !== null && meta !== void 0 ? meta : {})), (projections ? { 'pn-projections': projections } : {}));
body.permissions = permissions;
return JSON.stringify(body);
}
/**
* Serialize DataSync entity-level permissions into a resources / patterns target.
*
* The `datasync:*` wire keys are only written when their scope map is non-empty, so tokens that
* don't use DataSync stay byte-for-byte identical.
*
* @param dataSync - User provided DataSync permission scopes.
* @param target - Resources or patterns payload to populate.
* @param mapPermissions - Helper which writes a single bit-encoded permission into the target.
*/
mapDataSyncPermissions(dataSync, target, mapPermissions) {
if (!dataSync)
return;
const dataSyncScopes = [
['entities', 'datasync:entities'],
['relationships', 'datasync:relationships'],
['memberships', 'datasync:memberships'],
];
dataSyncScopes.forEach(([scope, wireKey]) => {
const scopePermissions = dataSync[scope];
if (!scopePermissions)
return;
Object.keys(scopePermissions).forEach((id) => mapPermissions(id, this.extractPermissions(scopePermissions[id]), wireKey, target));
});
}
/**
* Build the `pn-projections` meta payload from DataSync projection parameters.
*
* Each projection scope is encoded into a flat composite key (`datasync:<type>:<id>`) mapped to
* the projection name. The `res` / `pat` sub-objects are omitted when empty.
*
* @returns Encoded projections payload, or `undefined` when no projections are set.
*/
buildProjections() {
const projections = 'dataSyncProjections' in this.parameters ? this.parameters.dataSyncProjections : undefined;
if (!projections)
return undefined;
const encodeScope = (scope) => {
const encoded = {};
if (!scope)
return encoded;
['entities', 'relationships', 'memberships'].forEach((type) => {
const assignments = scope[type];
if (assignments)
Object.keys(assignments).forEach((id) => (encoded[`datasync:${type}:${id}`] = assignments[id]));
});
return encoded;
};
const result = {};
const res = encodeScope(projections.resources);
const pat = encodeScope(projections.patterns);
if (Object.keys(res).length > 0)
result.res = res;
if (Object.keys(pat).length > 0)
result.pat = pat;
return Object.keys(result).length > 0 ? result : undefined;
}
/**
* Extract permissions bit from permission configuration object.
*
Expand All @@ -157,6 +222,8 @@ class GrantTokenRequest extends request_1.AbstractRequest {
permissionsResult |= 64;
if ('get' in permissions && permissions.get)
permissionsResult |= 32;
if ('create' in permissions && permissions.create)
permissionsResult |= 16;
if ('delete' in permissions && permissions.delete)
permissionsResult |= 8;
if ('manage' in permissions && permissions.manage)
Expand Down
65 changes: 65 additions & 0 deletions lib/core/endpoints/data_sync/channel/create.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"use strict";
/**
* Create Channel REST API module.
*
* @internal
*/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CreateChannelRequest = void 0;
const transport_request_1 = require("../../../types/transport-request");
const request_1 = require("../../../components/request");
const operations_1 = __importDefault(require("../../../constants/operations"));
// endregion
/**
* Create Channel request.
*
* @internal
*/
class CreateChannelRequest extends request_1.AbstractRequest {
constructor(parameters) {
super({ method: transport_request_1.TransportMethod.POST });
this.parameters = parameters;
}
operation() {
return operations_1.default.PNCreateChannelOperation;
}
parse(response) {
return __awaiter(this, void 0, void 0, function* () {
// The DataSync service returns the object envelope ({ data } or { data, meta }) without a
// top-level HTTP status; surface `response.status` so callers can inspect it (parity with remove).
const parsed = this.deserializeResponse(response);
return Object.assign(Object.assign({}, parsed), { status: response.status });
});
}
validate() {
if (!this.parameters.channel)
return 'Channel cannot be empty';
if (this.parameters.channel.entityClassVersion === undefined || this.parameters.channel.entityClassVersion === null)
return 'Entity class version cannot be empty';
}
get headers() {
var _a;
const headers = (_a = super.headers) !== null && _a !== void 0 ? _a : {};
return Object.assign(Object.assign({}, headers), { 'Content-Type': 'application/vnd.pubnub.objects.channel+json;version=1' });
}
get path() {
const { keySet: { subscribeKey }, } = this.parameters;
return `/v1/datasync/subkeys/${subscribeKey}/channels`;
}
get body() {
return JSON.stringify({ data: this.parameters.channel });
}
}
exports.CreateChannelRequest = CreateChannelRequest;
Loading
Loading