How to separate Realm Object into two and get them into one variable? - realm

My goal to divide Realm.Object, so we can reuse specific object in different place. To reduce storage use.
The practical use would be:
call realm.objects("ChatList") to get the lastMessage value, without the need to get all of Message objects.
e.g reusing User Object. A User Object can be used in Message Object and ChatList Object.
User Object
const UserSchema: Realm.Object = {
name: "User",
properties: {
user: "string",
displayName: "string",
}
}
Message Object
const MessageObject: Realm.Object = {
name: "Message",
properties: {
user: "User",
message: "string",
}
}
ChatList Object
const ChatListSchema: Realm.Object = {
name: "ChatList",
properties: {
name: "string",
lastMessage: "Message",
}
}
What I've did:
I have read about Realm Data Model > Relationsihp. It requires to explicitly define it in the properties
e.g.
const User: Realm.Object = {
name: "User",
properties: {
name: "string",
tasks: "Task[]",
}
};
const Tasks: realm.Object = {
name: "Task",
properties: {
text: "string",
}
}
However, I am not sure how to link Task to other object. e.g if I use it like this
const write = () => {
realm.write(() => {
realm.create("User", {
name: "Jack",
tasks: [
{
text: "Say 1",
},
],
});
});
}

Related

How to check resource access rights via team membership in firebase security rules?

A user should be allowed to access a resource if they are in a team that is allowed to access the resource.
How can I do this in security rules ?
I have collections:
teams, with a .members field in each
resources, with a teamsThatCanAccess for each
If i wrote this in js, itd be something like:
canUserAccess = (userId, resource) => {
teams = resource.teamsThatCanAccess
hasAccess = false
teams.forEach((team) => {
if (userId in team.members) {
hasAccess = true
}
}
return hasAccess
}
However, as I understand it, security rules dont like loops.
--EDIT--
To illustrate further, the database I'm building will look like something like this:
teams = [
{ name: "teamA", org: "org1", members: ["uid1", "uid2", "uid3"] },
{ name: "teamB", org: "org1", members: ["uid1", "uid2"] },
{ name: "teamC", org: "org1", members: ["uid3", "uid4", "uid5"] },
{ name: "teamD", org: "org2", members: ["uid201", "uid202"] },
]
resources = [
{
id: "projectId1",
name: "project 1",
org: "org1",
teamsThatCanAccess: ["teamA", "teamB"],
},
{
id: "projectId2",
name: "project 2",
org: "org1",
teamsThatCanAccess: ["teamA", "teamB", "teamC"],
},
{
id: "projectId3",
name: "project 3",
org: "org1",
teamsThatCanAccess: ["teamC"],
},
{
id: "projectId4",
name: "project 201",
org: "org2",
teamsThatCanAccess: ["teamD"],
},
]
projectFiles = [
{ content: "document text", project: "projectId1" },
{ content: "document text 2", project: "projectId1" },
{ content: "document text 3", project: "projectId2" },
]
Based on what you described, you have a structure that looks like this:
// document at /teams/someTeamId
{
"members": [
"uid1",
"uid2",
"uid3"
],
/* ... */
}
// document at /resources/someResourceId
{
"teamsThatCanAccess": [
"someTeamId",
"otherTeamId"
],
/* ... */
}
To secure the data, you will need to introduce a new collection of documents, called something like teamsByUser:
// document at /teamsByUser/uid1
{
"memberOf": [
"someTeamId",
"otherTeamId"
]
}
By introducing this array, you can now use the rules.List#hasAny method to find if there is any overlap between the memberOf array in /teamsByUser/{userId} and the teamsThatCanAccess array in /resources/{resourceId}.
This will then allow you to configure your rules as
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /resources/{resourceId} {
allow read: if resource.data.size() == 0 // is empty?
|| get(/databases/$(database)/documents/teamsByUser/$(request.auth.uid)).data.memberOf.hasAny(resource.data.teamsThatCanAccess); // accessing user is a member of an allowed team
}
// don't forget to add rules to prevent users joining arbitrary teams from clients
}
}

How should I filter data in Strapi by $and operator

I want to filter my table data by createdAt,status_invitor and status_invited. I write query Like this
const query = qs.stringify({
filters: {
$and: {
createdAt: { $gte: firstDayOfTheMonth },
createdAt: { $lt: lastDayOfTheMonth },
},
$and: {
status_invitor: statusFilter
},
status_invited: statusFilter,
},
});
but is not working correctly
The filters are combined by default so unless I have misunderstood what you are trying to achieve, you don't need to use the $and operator.
const query = qs.stringify({
filters: {
createdAt: { $gte: firstDayOfTheMonth },
createdAt: { $lt: lastDayOfTheMonth },
status_invitor: statusFilter,
status_invited: statusFilter,
},
});
If you do want to mix AND and OR filters then you need to specify an array. You can see examples of that here: https://docs.strapi.io/developer-docs/latest/developer-resources/database-apis-reference/rest/filtering-locale-publication.html#complex-filtering
I'm working on a project where I wanted to query more than one field with a search term. This is the code I used to get it working. I think all you need to do is change the $or for the $and and it should work.
const query = qs.stringify( {
filters: {
$or: [
{ name: { $contains: term }},
{ venue: { $contains: term } },
{ performers: { $contains: term } },
{ description: { $contains: term } },
],
},
},
)

Can't update Array of objects

I'm trying to create an array of objects using simple-schema. In this example, a person has a careerHistory object that is filled with individual positions. I can't figure out how to insert and update the array of objects. It just errors. The only way I can get it to work is to be explicit, eg. 'careerHistory.position.1.company'.
I'm using:
meteor
simple-schema
reactjs
collection2-core
Path: mongodb
const ProfileCandidateSchema = new SimpleSchema({
userId: {
type: String,
regEx: SimpleSchema.RegEx.Id
},
careerHistory: { type: Array, optional: true },
'careerHistory.position': { type: Object, optional: true },
'careerHistory.position.$': { type: Object, optional: true },
'careerHistory.position.$.company': { type: String, optional: true },
'careerHistory.position.$.title': { type: String, optional: true }
});
Path: updateForm.js
ProfileCandidate.update(id, { $set: {
'careerHistory.position.company': this.state['position.company'],
'careerHistory.position.title': this.state['position.title'],
}
});
If you want to push object to array do
ProfileCandidate.update(id,
{ $push: { careerHistory: { position: {
'company': this.state['position.company'],
'title': this.state['position.title'],
}}}
});
if you want to update particular object
ProfileCandidate.update({ _id: id, 'careerHistory.position.company': this.state['position.company'] }, { $set: {
'careerHistory.position.$.title': this.state['position.title'],
}
});
see $ in set

how to extend faceted search by passing extra argument to the url in alfresco 5.1.1

We want to customize the faceted search by passing extra argument in the faceted search URL and read it in org\alfresco\slingshot\search\search.get.js---->search.lib.js.
http://localhost:8080/share/page/dp/ws/faceted-search#searchTerm=Koala.jpg&scope=repo&nodeRef=test
In searchDocLib json ,we have nodeRef value assigned it to selectedContainer but that argument is not coming in search.get.js. Basically how to pass extra argument in searchDocLib?How to enable logs for faceted-search.get.js so that logger statements should be printed in share.log?
var noderef = (page.url.args["nodeRef"] != null) ? page.url.args["nodeRef"] : "";
logger.log(page.url.templateArgs.nodeRef+"....nodeRef = "+nodeRef);
// Build the searchDocLib model
var searchDocLib = {
id: "FCTSRCH_SEARCH_RESULTS_LIST",
name: "alfresco/documentlibrary/AlfSearchList",
config: {
viewPreferenceProperty: "org.alfresco.share.searchList.viewRendererName",
view: viewRendererName,
waitForPageWidgets: true,
useHash: true,
useLocalStorageHashFallback: true,
hashVarsForUpdate: [
"searchTerm",
"facetFilters",
"sortField",
"sortAscending",
"query",
"scope",
"selectedContainer"
],
selectedScope: "repo",
useInfiniteScroll: true,
siteId: null,
rootNode: repoRootNode,
repo: false,
selectedContainer: noderef,
additionalControlsTarget: "FCTSRCH_RESULTS_MENU_BAR",
additionalViewControlVisibilityConfig: hideOnZeroResultsConfig,
widgets: [
{
id: "FCTSRCH_SEARCH_ADVICE_NO_RESULTS",
name: "alfresco/documentlibrary/views/AlfSearchListView",
config: {
widgetsForNoDataDisplay: widgetsForNoDataDisplay,
a11yCaption: msg.get("faceted-search.results.caption"),
a11yCaptionClass: "hiddenAccessible",
widgetsForHeader: [
{
id: "FCTSRCH_THUMBNAIL_HEADER_CELL",
name: "alfresco/documentlibrary/views/layouts/HeaderCell",
config: {
label: msg.get("faceted-search.results.heading.thumbnail"),
class: "hiddenAccessible",
a11yScope: "col"
}
},
{
id: "FCTSRCH_DETAILS_HEADER_CELL",
name: "alfresco/documentlibrary/views/layouts/HeaderCell",
config: {
label: msg.get("faceted-search.results.heading.details"),
class: "hiddenAccessible",
a11yScope: "col"
}
},
{
id: "FCTSRCH_ACTIONS_HEADER_CELL",
name: "alfresco/documentlibrary/views/layouts/HeaderCell",
config: {
label: msg.get("faceted-search.results.heading.actions"),
class: "hiddenAccessible",
a11yScope: "col"
}
}
],
widgets: [
{
id: "FCTSRCH_SEARCH_RESULT",
name: "alfresco/search/AlfSearchResult",
config: {
enableContextMenu: false
}
}
]
}
},
{
id: "FCTSRCH_GALLERY_VIEW",
name: "alfresco/documentlibrary/views/AlfGalleryView",
config: {
showNextLink: true,
nextLinkLabel: msg.get("faceted-search.show-more-results.label"),
widgetsForNoDataDisplay: widgetsForNoDataDisplay,
widgets: [
{
id: "FCTSRCH_GALLERY_VIEW_THUMBNAIL_DOC_OR_FOLDER",
name: "alfresco/search/SearchGalleryThumbnail",
config: {
widgetsForSelectBar: [
{
id: "FCTSRCH_GALLERY_VIEW_MORE_INFO_OR_FOLDER",
name: "alfresco/renderers/MoreInfo",
align: "right",
config: {
filterActions: true,
xhrRequired: true
}
}
],
publishTopic: "ALF_NAVIGATE_TO_PAGE",
renderFilter: [
{
property: "type",
values: ["document","folder"],
negate: false
}
]
}
},
{
id: "FCTSRCH_GALLERY_VIEW_THUMBNAIL_OTHER",
name: "alfresco/search/SearchGalleryThumbnail",
config: {
widgetsForSelectBar: [
{
id: "FCTSRCH_GALLERY_VIEW_MORE_INFO_OTHER",
name: "alfresco/renderers/MoreInfo",
align: "right",
config: {
filterActions: true,
allowedActionsString: "[\"document-delete\"]",
xhrRequired: true
}
}
],
publishTopic: "ALF_NAVIGATE_TO_PAGE",
renderFilter: [
{
property: "type",
values: ["document","folder"],
negate: true
}
]
}
}
]
}
},
{
id: "FCTSRCH_INFINITE_SCROLL",
name: "alfresco/documentlibrary/AlfDocumentListInfiniteScroll"
}
]
}
};
I've written a blog post that covers customizing the search page. Although it isn't exactly the same use case, the principle remains the same - you're going to want to create your own SearchService (extending the default one) and then swap yours for the default one in the faceted-search page model. You'll want to extend the onSearchRequest function to include the extra request parameter.

SAPUI5: Getting argument as undefined, passed while routing from one view to another

I am passing arguments from Master to Detail view while routing, but getting it as undefined.
Code from MasterController.js
onPressItemDetail: function(evt) {
var object = evt.getSource().
getBindingContext().
getModel().
getProperty(evt.getSource().getBindingContext().getPath());
var context = {
object: object,
bindingContext: evt.getSource().getBindingContext()
};
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.navTo("fourth", context);
}
Code from DetailController.js
onInit: function() {
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.attachRouteMatched(function(oEvent) {
if (oEvent.getParameter("name") !== "fourth") {
return;
}
var object = oEvent.getParameter("arguments").object;
var bindingContext = oEvent.getParameter("arguments").bindingContext;
}, this);
}
Routing configuration from Component.js
"routing": {
"config": {
"routerClass": "sap.m.routing.Router",
"viewType": "XML",
"viewPath": "umicoreMP.view",
"controlId": "app",
"controlAggregation": "pages"
},
"routes": [{
"pattern": "",
"name": "first",
"target": "first"
}, {
"pattern": "secondview",
"name": "second",
"target": "second"
}, {
"pattern": "thirdview",
"name": "third",
"target": "third"
}, {
"pattern": "changeitem",
"name": "fourth",
"target": "fourth"
}],
"targets": {
"first": {
"viewName": "FirstView"
},
"second": {
"viewName": "SecondView"
},
"third": {
"viewName": "ThirdView"
},
"fourth": {
"viewName": "ChangeItem"
}
}
}
Route pattern must contain the desired parameter in the following format:
{
"pattern": "changeitem/{object}",
"name": "fourth",
"target": "fourth"
}
oRouter.navTo() contains the desired route and an object which contains the parameters of the route:
oRouter.navTo("fourth", {object: property});
Passing the bindingcontext as a parameter is not recommended (and in this case, not possible) because it's an object and it's not safe to pass object like this in URL.
If you want to access the data which belongs to the property passed to the Detail view, you can fetch it from the model.
Assuming that your model is assigned to the component, each view has access to that. Here you can bind the proper entry of the model to the view in several ways, for example:
oRouter.attachRouteMatched(function (oEvent) {
if (oEvent.getParameter("name") !== "fourth") {
return;
}
var object = oEvent.getParameter("arguments").object;
this.getView().bindElement({path: object, model: "nameOfTheModel"});
}
The exact value of the path depends on the model (JSONModel or ODataModel). Model name should be defined in component.js.
With this bindElement function call you can assign the specific row of the model to the view, so controls in the view can access properties of the selected data entry with relative binding:
<Text text="{propertyInModel}" />

Resources