IBM Domino 10 - integrating with Resource Reservations via Domino Data Services API - api
We're trying to integrate with IBM Domino via REST API to pull out information about reservations/events in a specific room and also be able to create new events/reservations remotely. We already integrated with other services such as Microsoft Exchange, but IBM seems to be the toughest of them all.
I studied deeply into it and read thousands of articles & stack overflow questions, and got pretty far but still can't make any real use out of it.
What I currently plan on doing is this:
Pull information about reservations from /api/data/collections/name/($Reservations) or ($Calendar)
Create events/reservations using the documents api, POSTing to /api/data/documents?form=Reservation, I already tried doing it and my reservation even showed in Domino Admin (not in Notes client though), but it had some errors (probably just some json problem on my side)
While it looks kinda clear and easy, it really isn't. I have a few questions:
How can I get reservations/calendar for a specific room? ($Calendar) returns all events in the database, not even including in which room it is, to get that information I would need to additionally query each reservation by it's unid and that would probably kill the entire app
Is there any way I could filter/search the /api/data/documents to return only documents whose form field has a value of Reservation or any other value? This way I could get all the reservation documents without querying each of the documents directly (/api/data/documents only returns the href to the document without any interesting data), I wouldn't also need to additionally enable DAS for each view I want to use.
What are the fields like $25 returned in the json, and how can I know what's their purpose if they don't have any real name? They often contain interesting data, such as the room name.
I also looked into the FreeBusy api service, and it's pretty interesting and I could easily use it to look for reservations (/busytimes) in the room I want, if it ever returned what resource/reservation is causing the busy time. It just shows the start and end time, nothing else..
I also read suggestions that one should create a 'main' user to handle the reservations and use his calendar api (/api/calendar/events), but afaik it can't be done that way.
However I tried creating events in the users calendar in specified room, and kinda got it to work by adding the following attendee in the json |(PHP syntax, actually):
'organizer' => [ 'email' => 'admin/test#test.com' ],
'attendees' => [
[
'role' => 'req-participant',
'userType' => 'room',
'status' => 'accepted',
'rsvp' => true,
'email' => 'testroom#test.com',
],
],
But it doesn't really get displayed in the room reservations, unlike normal events created in IBM Notes. It also cannot be edited or deleted in IBM Notes, and it has "Accepted: " in front of the subject, and it says "attendance is delegated for admin". To delete it, I need to delete it via API through its unid directly. x-lotus-noticetype is being set to A so I guess it's not being treated as a meeting but as an notice, no idea why though.
I'd really like some help or suggestions on how I could get this working, are there any other ways that would have any sense?
Edit:
After struggling a lot and reading Dave's reply, I think it would be a good solution to have a single user that would do the reservations via calendar api, because the direct data api probably won't work. I could just only pull the list of all reservations from Rooms database ($Calendar) or ($Reservations) view, or make some sort of my own view.
However! I cannot get the calendar method to work on my local IBM Domino server. Dave pointed to me that I need to specify a valid email (internet address) of the organizer, so I set my user's internet address to testmail#test.test (test.test is mapped to 127.0.0.1 in the hosts file). Now as soon as I try to use that address like that:
"organizer": {
"email": "testmail#test.test"
}
I cannot even create the event/reservation (through /mail/admin.nsf/api/calendar/events), it's returning 500 internal error with cserror 1026, and Domino logs
[CS API]> Error | calendarapi.c(379) : There was an error sending out notices to meeting participants. (0x8E4)
Error connecting to server test/test: The remote server is not a known TCP/IP host.
So it has a problem with sending the notice, and doesn't create the event at all. I thought it may not work with localhost, so I set my users email to an external mail service, and I even received the email, but the event was still created incorrectly (x-lotus-noticetype A is being added automatically and overrides whatever I send as the value), it's not visible in the Room Reservations database.
Here's the json object of an event created via Notes client:
"events": [
{
"href":"\/mail\/admin.nsf\/api\/calendar\/events\/2B35FABBC50EA4D0C12583BC002E26FA-Lotus_Notes_Generated",
"id":"2B35FABBC50EA4D0C12583BC002E26FA-Lotus_Notes_Generated",
"summary":"Notes client meeting",
"location":"Test room\/Test site#test",
"start": {
"date":"2019-03-13",
"time":"09:30:00",
"tzid":"Central European Standard Time"
},
"end": {
"date":"2019-03-13",
"time":"10:30:00",
"tzid":"Central European Standard Time"
},
"class":"public",
"transparency":"opaque",
"sequence":0,
"last-modified":"20190313T082436Z",
"attendees": [
{
"role":"chair",
"status":"accepted",
"rsvp":false,
"displayName":"admin\/test",
"email":"testmail#test.test"
},
{
"role":"req-participant",
"userType":"room",
"status":"needs-action",
"rsvp":true,
"displayName":"Test room\/Test site",
"email":"room#test.test"
}
],
"organizer": {
"displayName":"admin\/test",
"email":"testmail#test.test"
},
"x-lotus-broadcast": {
"data":"FALSE"
},
"x-lotus-notesversion": {
"data":"2"
},
"x-lotus-appttype": {
"data":"3"
}
}
]
As you can see, Notes is able to create the event with testmail#test.test successfully.
Now here's an event created with my API, but with admin/test#test.test as the organizer's email (because normal email doesn't let me create the event):
"events": [
{
"href":"\/mail\/admin.nsf\/api\/calendar\/events\/E1D1F752203FC2DFC12583BC002FCB12-Lotus_Auto_Generated",
"id":"E1D1F752203FC2DFC12583BC002FCB12-Lotus_Auto_Generated",
"summary":"Api reservation test",
"location":"Test room\/Test site#test\r\nCN=Test room\/O=Test site",
"description":"API Generated event\r\n",
"start": {
"date":"2019-03-20",
"time":"11:00:00",
"utc":true
},
"end": {
"date":"2019-03-20",
"time":"15:00:00",
"utc":true
},
"class":"public",
"transparency":"opaque",
"sequence":0,
"last-modified":"20190313T084201Z",
"attendees": [
{
"role":"chair",
"status":"accepted",
"rsvp":false,
"displayName":"admin\/test",
"email":"testmail#test.test"
},
{
"role":"req-participant",
"userType":"room",
"status":"needs-action",
"rsvp":true,
"displayName":"Test room\/Test site",
"email":"room#test.test"
}
],
"organizer": {
"displayName":"admin\/test",
"email":"testmail#test.test"
},
"x-lotus-broadcast": {
"data":"FALSE"
},
"x-lotus-notesversion": {
"data":"2"
},
"x-lotus-noticetype": {
"data":"A"
},
"x-lotus-appttype": {
"data":"3"
}
}
]
As you can see, the organizer's & chair emails were automatically updated by Lotus to testmail#test.test, and theorethically everything should work but it doesnt. In Notes, I see the event as 'Accepted: Api reservation test' and I cannot modify things like the room, or don't have the option to delete it from right click menu (I can delete it with Del keyboard button though)
The only difference is that x-lotus-noticetype get's added, and I don't even know why
Edit 2:
I got it to work! Dave pointed that I may have some configuration issue, so I re-installed the server & setup everything again (including the mail services), I used admin#test.test and the meeting was succesfully created & added to the room reservations. Server console only showed that the message was delivered.
HOWEVER! I was able to create as many identical meetings as I wanted, they weren't added to the reservations database but they were succesfully created in my calendar (with the room assigned to them) without any errors (not even in the sever console), this is obviously bad. Is there any way to check (externally, through API) if the reservation was created succesfully, and prevent it's creation if the room is busy at that moment? Notes client prompts an error when the room is busy. I could probably use FreeBusy api, however that would require another HTTP request before each reservation attempt, but if that's the only way then I'll just take it. I see that the status field of the attendeed room is set to declined, but the response from POST still contains needs-action so I'd need to do some delayed request once again to check if the status has changed to declined or not.
Also, while it works, I still don't know how I could obtain a list of reservations in a selected room? The already existing views in Reservations database don't give many details, and they need to be exclusively enabled DAS services in order to work. Is there any other way that could work properly?
Another thing is, is there any way I could get current user's email address to use for the reservations, or can I only 'hardcode' it manually? Same goes for room's email. Currently, I need to have:
User name
User password
User mail database (/mail/admin.nsf/)
User email
Room email
and if I'd want to read some data from the Reservations database directly, then I'd also need to have the path to that database. This isn't really user-friendly, I'd like to automate some things if possible. Otherwise the integration may be impossible to make.
The reservation database was designed to manage reservations either (1) directly through it's own UI, or (2) indirectly by auto-processing notifications from calendar users. By using the DAS data API, you are asserting you can manage reservations (3) programmatically -- by manipulating the low-level document items. You might get this to work, but I don't think the reservation database was designed with that in mind.
That's why I think this answer is the best option. It leverages auto-processing (#2 above) and saves you from dealing with the internal design of reservation documents. If you use this approach, you should give the DAS calendar API a list of attendees like this:
"attendees":[
{
"role":"req-participant",
"userType":"room",
"status":"needs-action",
"rsvp":true,
"email":"room#mycorp.com"
}
]
In other words, status must be "needs-action" -- not "accepted" as shown in your original post. Also, make sure you are using the correct email address for both the organizer and the target room. The above example shows an Internet-style address for the room, but administrators don't always give a room an Internet address.
Related
Firebase Realtime DB: How to setup rule to only write to DB if value being passed to be written is not already stored in DB
I am looking for syntax to setup a rule in my Firebase Realtime DB to confirm that the value being passed from my app does not already exist in that particular location before writing another duplicate entry. I am passing an "ExponentPushToken" value for push notifications. This happens anonymously. That is, the write is happening without any user authentication, the DB is simply storing a list of "ExponentPushToken" like so: { "users" : { "push_token" : { "auto-generated key" : "ExponentPushToken-value" } } } I attempted to use the following, but it still will allow the "duplicate" value to be written. I am wondering if this has anything to do with random 'auto-generated key' that is generated for each instance a value written to the DB, or if i am not thinking through this properly. { "rules": { ".read": "false", ".write": "true && newData.val() !== data.child('push_token').val()", } } For context, each ExponentPushToken is a unique value for a given device that is used to open my app. The ExponentPushToken is a unique ID that is assigned to that device indefinitely and won't change ever in any case that particular device is accessing the app. The ExponentPushToken is assigned to the device indefinitely, and is written to the DB. Each time the app is opened, there is a push/set command to write that token to the DB within my app's code. I would like to setup a rule for Firebase Realtime DB to confirm the value being passed from the app to the DB does NOT get written if that value already exists. I am stuck and am finding myself having to manually parse the db to remove duplicate value to keep the DB clean and not send more than one Push Notification to app users. Any help or direction you can provide would be greatly appreciated! Thanks!
This cannot be done using firebase security rules, as they cannot search for values (that wouldn't scale). The query to check for existing values in a particular location in the database would have to be done in the app that is performing the push/writing of the value into the database. The app would need to query the database for values and have a condition in place to prevent the writing of duplicate values. An example of working code that can be implemented within the app can be found here that accomplishes the desired outcome for this specific ask/desired outcome. Expo and Firebase Realtime DB - Read values and confirm a particular value does not exist before writing to database
Rules are evaluated in the context of the node/path in which you declare them. So the .write rule in your question is evaluated on the root, and both data and newData contain the data/new data at the root. The easiest way to verify that there's no data at the path, is to define this rules at that path: { "rules": { ".read": "false", "push_token": { "$pushId": { ".write": "!data.exists()", } } } } The $pusId here means that the rules under there are applies to every child node of push_token, which seems to match your JSON. For more on this, see the documentation on wildcard variables.
Linking a Contact to a Customer using the SOAP API
I’m having an issue with linking a customer object to a pre-existing contact when creating a new customer within the SOAP/SuiteTalk API using the Mulesoft middleware. I’m successfully creating the contact object earlier, and then trying to use that internal ID to link that contact object to the newly created customer object. The relevant part I’m posting within the customer object (just hardcoding the internal ID in order to get it running): contactRolesList: { contactRoles: [ { contact: { "type": "CONTACT", internalId: '74335', }, role: { "type": "CONTACT_ROLE", internalId: ‘-10’, } } ] } When I attempt to upsert the customer object this way, I don’t get any errors in the integration role execution log, but the contact is never linked. If I try to upsert a second time, I get an error with the format: Unable to find a matching line for sublist contactroles with key: [contact] and value: [74335]. even though a contact object with that internal ID clearly exists. Am I going about this entirely the wrong way, or am I just narrowly missing the syntax here? *As a side note, is it possible to set the relationship on a RecordRef using just the external ID? As in, using just the external ID of the contact object in question to reference it and create the link? Thank you.
The "contactRolesList" field is only for updating contacts already connected to the customer. In order to link a contact with a customer you need to perform an attach operation. I'm not experienced with the Mulesoft middleware you mention, but in a soap request it would look like this: <attach xmlns="urn:messages_2017_1.platform.webservices.netsuite.com"> <attachReferece xsi:type="ns1:AttachContactReference" xmlns:ns1="urn:core_2017_1.platform.webservices.netsuite.com"> <ns1:attachTo internalId="176" type="customer" xsi:type="ns1:RecordRef"> </ns1:attachTo> <ns1:contact internalId="1467" xsi:type="ns1:RecordRef"/> <ns1:contactRole internalId="-10" xsi:type="ns1:RecordRef"> </ns1:contactRole> </attachReferece> </attach> You can get additional information in your netsuite CRM's help center at: SuiteCloud Platform/ SuiteTalk Web Services /SuiteTalk SOAP Web Services Platform Guide /SOAP Web Services Operations attach - detach
Getstream.io throws exception when using the "to" field
I have two flat Feed Groups, main, the primary news feed, and main_topics. I can make a post to either one successfully. But when I try to 'cc' the other using the to field, like, to: ["main_topics:donuts"] I get: code: 17 detail: "You do not have permission to do this, you got this error because there are no policies allowing this request on this application. Please consult the documentation https://getstream.io/docs/" duration: "0.16ms" exception: "NotAllowedException" status_code: 403 Log: The request didn't have the right permissions or autorization. Please check our docs about how to sign requests. We're generating user tokens server-side, and the token works to read and write to both groups without to. // on server stream_client.user(user.user_id).create({ name: user.name, username: user.username, }); Post body: actor: "SU:5f40650ad9b60a00370686d7" attachments: {images: [], files: []} foreign_id: "post:1598391531232" object: "Newsfeed" text: "Yum #donuts" time: "2020-08-25T14:38:51.232" to: ["main_topics:donuts", "main_topics:all"] verb: "post" The docs show an example with to: ['team:barcelona', 'match:1'], and say you need to create the feed groups in the panel, but mention nothing about setting up specific permissions to use this feature. Any idea why this would happen? Note that I'm trying to create the new topics (donuts, all) which don't exist when this post is made. However, the docs don't specify that feeds need to be explicitly created first - maybe that's the missing piece?
If you haven’t already tried creating the feed first, then try that. Besides that, the default permissions restrict a user from posting on another’s feed. I think it’s acceptable to do this if it’s a notification feed but not user or timeline. You can email the getstream support to change the default permissions because these are not manageable from the dashboard. Or you can do this call server side as an admin permissions.
What if access control rule defined for participant/asset contradicts access control rule for transaction?
I have a question regarding access control. Specifically, the question is about the relationship between access control rules defined for participants or assets on the one hand and asset control rules defined for transactions accessing those participants/assets. Here is an example: Assume a Hyperledger Fabric network is used to create some kind of social network for employees of a company. The following rule states that an employee has write access to his own data: rule EmployeesHaveWriteAccessToTheirOwnData { description: "Allow employees write access to their own data" participant(p): "org.company.biznet.Employee" operation: UPDATE resource(r): "org.company.biznet.Employee" condition: (p.getIdentifier() == r.getIdentifier()) action: ALLOW } Let's assume that the write access is facilitated through a transaction called "UpdateTransaction". Further assume that (maybe by accident) the action value of the access control rule of transaction "UpdateTransaction" is set to "Denied" rule EmployeeCanSubmitTransactionsToUpdateData { description: "Allow employees to update their data" participant: "org.company.biznet.Employee" operation: CREATE resource: "org.company.biznet.UpdateTransaction" action: Denied } Now there is the following situation: Each employee is (through rule 1) given the right to change his/her data. At the same time employees are not allowed to submit the transaction "UpdateTransaction" to change the data (see rule 2). Is it now impossible for employees to change their data? Or are employees still able to change their data without submitting the transaction "UpdateTransaction"? Put differently: is there a way for participants to access data (for which they have access rights) without using any of the transactions defined in the .cto-file?
I think the answer is, it depends. In your example, denying access to the org.company.biznet.UpdateTransaction transaction would result in org.company.biznet.Employee participants being unable to use that transaction to update their data, even though they would otherwise be allowed. Having said that, you should keep the system transactions in mind since they provide another potential route for org.company.biznet.Employee participants to update their own data. For example, I tried that out on the basic-sample-network by replacing the EverybodyCanSubmitTransactions rule with rule NobodyCanSubmitTransactions { description: "Do not allow all participants to submit transactions" participant: "org.example.basic.SampleParticipant" operation: CREATE resource: "org.example.basic.SampleTransaction" action: DENY } That business network includes an OwnerHasFullAccessToTheirAssets rule and I was able to use the org.hyperledger.composer.system.UpdateAsset transaction to make updates for participants that owned an asset using the command, composer transaction submit -d "$(cat txn.json)" -c party1#basic-sample-network Where txn.json contained, { "$class": "org.hyperledger.composer.system.UpdateAsset", "resources": [ { "$class": "org.example.basic.SampleAsset", "assetId": "ASSET1", "owner": "resource:org.example.basic.SampleParticipant#PARTY1", "value": "5000" } ], "targetRegistry": "resource:org.hyperledger.composer.system.AssetRegistry#org.example.basic.SampleAsset" } That wouldn't work if you had locked down the system namespace in your ACL rules though. (ACLs need a lot of thought!) The other important thing to remember about ACLs is that they do not apply if you use the getNativeAPI method to access data via the Hyperledger Fabric APIs in your transaction processor functions. Check out the system namespace reference along with the ACL reference, plus there is an ACL tutorial which may be of interest if you haven't seen it.
slashDB accessing a database via POST request and using APIkey yields 403 error
Question about security for POST method of HTTP: I made a user called "MyAPP": { "userdef": [ "view", "create" ], "api_key": "dzn8k7hj2sdgddlvymfmefh1k2ddjl05", "user_id": "MyAPP", "name": "MyAPP", "creator": "admin", "edit": [], "dbdef": [ "view", "create" ], "querydef": [ "view", "create" ], "databases": { "Gaming": { "dbuser": "mydbuser_here", "dbpass": "mypass_here" } }, "password": "$6$rounds=665736$x/Xp0k6Nj.5qzuM5$G.3w6Py1s.xZ83RHDU55qonNMpJe4Le8nD8PqjYKoOtgbab7T22knJPqwHspoT6BQxp.5gieLFuD0SdD9dyvi/", "email": "", "view": [] } Then I wanted to issue a POST in order to execute a SQL Pass-thru such as this: http:///query/InsertBestScore/Score/99/ScreenName/GollyGolly.xml?apikey=dzn8k7hj2sdgddlvymfmefh1k2ddjl05 Where I built a query and named it "InsertBestScore": insert into Gaming.Leaderboard (ScreenName, Score) values (:ScreenName, :Score); If I run this via POSTMAN using the POST method: ... then I get an access, 403 : <?xml version="1.0" encoding="utf-8"?> <SlashDB> <http_code>403</http_code> <description>Access was denied to this resource. Please log in with your username/password or resend your request with a valid API key.</description> <url_template>/query/InsertBestScore/Score/{Score}/ScreenName/{ScreenName}.xml</url_template> </SlashDB> Also, I would be calling this POST (or PUT) request from an application, in my case a Python program running from within a AWS Lambda Function. Now, I came across this in the documentation: Two parameters API key SlashDB also allows a two parameters credentials in this authentication method - app id and api key. This may come handy when integrating with API management systems like 3Scale. By default header and query string argument would be: • appid - identifies certain application • apikey - secret for the application Request with API key in header - Access granted ... however in the example above, I don't see where the appid comes into play. Can you tell me how one would call the SlashDB endpoint and pass a APIkey and assure that the userid is known as MyAPP. So, to sum up, the Documentation mentions: • Another application utilizes an API key to authenticate, which is sent with every request. The application is recognized as SlashDB user App2, which uses database login db_admin. Effectively this application can SELECT, UPDATE, INSERT and DELETE data. So I want to actually, do just what is in that bullet: Identify myself as the user (instead of App2, I'm user MyAPP), and then use the dbuser and dbpass that was assigned to access that "Gaming" database. Idea?
Make sure you've given user MyAPP permission to execute the query. To do so: login as admin, go to Configure -> Queries, open your query definition, update field Execute. It accepts comma separated user ids.
OK, there are really two questions here: Why was access denied? What is the appid and how to use it. Ad. 1: There are two authorization barriers that the request has to clear. The first one is imposed by SlashDB in that the user executing the query must be listed in the Execute field on the query definition screen. This is done under Configure -> Queries -> "edit" button on your query. The second barrier is imposed by the database. The SlashDB user who is executing your POST request must be mapped to a physical database user with INSERT privileges to the Gaming.Leaderboard table. It goes without saying that this database user must be associated with the database schema in which the table exists. Ad. 2. To enable the appid the user api key must be composed out of two parts separated by colon ":". The first part will be interpreted as the appid and the second will be the apikey. To do that, use Configuration -> Users -> 'edit' button for the user in question. Then simply add a colon at the beginning of the API key and type in your desired appid to the left of the colon. The app will have to supply both keys to execute the request. Note that the names of those keys (i.e. appid) are configurable in /etc/slashdb/slashdb.ini. The reasoning behind this feature is to facilitate API Management platforms, which can help with key management, especially when API will be exposed to third party developers.