My team is looking to get view activity data for some google docs which are shared out organization-wide. Ultimately we want usage stats for these docs. It would be nice to be able to count views for copies of these docs too, but we'd be happy with just the views on the original docs to start.
I've tried to find view details somewhere within the document methods to no avail (I found the getViewers but that won't help me here).
I've also looked into binding a script to the documents to count when a user opens it but onOpen only triggers for users with edit access -- most of the people opening these docs will be opening with read-only access.
Any suggestions?
You can use activities.list in Reports API to access Drive audit logs which contains view log activities and other events. See Drive Audit Activity Events for a list of events available.
Example Using Admin Console:
Sample activities.list Request Parameters:
userKey: all
applicationName: drive
eventName: view
filters: doc_id==13NgKy87BggedOXnmkymygTyEh0xxxxxxxx
Sample Response: (Removed some sensitive information)
{
"kind": "admin#reports#activities",
"etag": "\"gwJsVSTi6OzvUGrf7ei0V53d9qZWZz_Kvb9LYWitNI4/sGVnptbK063qxxxxxx\"",
"items": [
{
"kind": "admin#reports#activity",
"id": {
"time": "2021-03-24T18:49:24.579Z",
.....
},
"actor": {
"email": "user1#domain.com",
"profileId": "1043405157872289xxxxx"
},
"events": [
{
"type": "access",
"name": "view",
......
}
]
},
{
"kind": "admin#reports#activity",
"id": {
"time": "2021-03-24T18:49:01.942Z",
.....
},
"actor": {
"email": "rm#domain.com",
"profileId": "1146312647848028xxxxx"
},
"ipAddress": "110.54.238.28",
"events": [
{
"type": "access",
"name": "view",
......
}
]
},
{
"kind": "admin#reports#activity",
"id": {
"time": "2021-03-24T18:48:29.751Z",
.....
},
"actor": {
"email": "",
"profileId": "1052505060979797xxxxx"
},
"events": [
{
"type": "access",
"name": "view",
......
}
]
},
{
"kind": "admin#reports#activity",
"id": {
"time": "2021-03-24T18:47:30.288Z",
.....
},
"actor": {
"email": "rm#domain.com",
"profileId": "1146312647848028xxxxx"
},
"ipAddress": "110.54.238.28",
"events": [
{
"type": "access",
"name": "view",
......
}
]
},
{
"kind": "admin#reports#activity",
"id": {
"time": "2021-03-22T21:17:35.009Z",
.....
},
"actor": {
"email": "rm#domain.com",
"profileId": "114631264784802xxxxx"
},
"events": [
{
"type": "access",
"name": "view",
......
}
]
}
]
}
NOTE:
If the file was viewed by a user outside your organization, the email address will not be available (User is anonymous)
Drive audit logs has a data retention time of 6 months. You can access Drive audit logs data this far back. You might want to consider saving your data count probably on a monthly basis so that if ever previous drive audit logs were removed you still have the data with you.
References:
View user Google Drive file activity
Data retention and lag times
Related
I’m reading through the tutorial (https://forge.autodesk.com/en/docs/data/v2/tutorials/publish-model/) to automatically publish projects, and having troubles when using GET hubs –
I’m currently using postman, and set up a 3 legged auth token with data:read, data:write, and data:create using the example here - https://forge.autodesk.com/blog/3-legged-authentication-postman
I get the correct response back using GET users/#me (see below). So I think that the authentication is working properly.
"userId": "**OBSCURED**",
"userName": "shane#**OBSCURED**",
"emailId": "shane#**OBSCURED**",
"firstName": "Shane",
"lastName": "**OBSCURED**",
"emailVerified": true,
"2FaEnabled": false,
"countryCode": "US",
"language": "en",
"optin": false,
"lastModified": "2020-09-08T19:31:48.802",
"profileImages": {
"sizeX20": "https://s3.amazonaws.com:443/com.autodesk.storage.public.production/oxygen/**OBSCURED**/profilepictures/x20.jpg?r=**OBSCURED**",
"sizeX40": "https://s3.amazonaws.com:443/com.autodesk.storage.public.production/oxygen/**OBSCURED**/profilepictures/x40.jpg?r=**OBSCURED**",
"sizeX50": "https://s3.amazonaws.com:443/com.autodesk.storage.public.production/oxygen/**OBSCURED**/profilepictures/x50.jpg?r=**OBSCURED**",
"sizeX58": "https://s3.amazonaws.com:443/com.autodesk.storage.public.production/oxygen/**OBSCURED**/profilepictures/x58.jpg?r=**OBSCURED**",
"sizeX80": "https://s3.amazonaws.com:443/com.autodesk.storage.public.production/oxygen/**OBSCURED**/profilepictures/x80.jpg?r=**OBSCURED**",
"sizeX120": "https://s3.amazonaws.com:443/com.autodesk.storage.public.production/oxygen/**OBSCURED**/profilepictures/x120.jpg?r=**OBSCURED**",
"sizeX160": "https://s3.amazonaws.com:443/com.autodesk.storage.public.production/oxygen/**OBSCURED**/profilepictures/x160.jpg?r=**OBSCURED**",
"sizeX176": "https://s3.amazonaws.com:443/com.autodesk.storage.public.production/oxygen/**OBSCURED**/profilepictures/x176.jpg?r=**OBSCURED**",
"sizeX240": "https://s3.amazonaws.com:443/com.autodesk.storage.public.production/oxygen/**OBSCURED**/profilepictures/x240.jpg?r=**OBSCURED**",
"sizeX360": "https://s3.amazonaws.com:443/com.autodesk.storage.public.production/oxygen/**OBSCURED**/profilepictures/x360.jpg?r=**OBSCURED**"
},
"ldapInfo": {
"ldapEnabled": false
},
"socialUserInfoList": []
}
When I try with the same token to get hubs (using the developer docs here https://forge.autodesk.com/en/docs/data/v2/reference/http/hubs-GET/) I get the response shown below. I expect to see the accounts listed in my docs.bim360.autodesk.com projects as shown in the screenshot below. I read online that sometimes provisioning needs to be done by Autodesk to get the right stuff to show up – it seems these are hubs that I’m no longer using and set up in the early days of our bim360 account (I believe these hubs are reflective of our very first b360 project, and a test project I set up).
I’ve also tried to get projects with the hub id’s listed below, but get 404 errors saying that they do not exist.
I am also not sure if the warnings at the end of the response are the two hubs I’m looking for, because I’m writing my request wrong, or something else. I get a 200 OK back, but it seems like what I’m looking for is missing.
{
"jsonapi": {
"version": "1.0"
},
"links": {
"self": {
"href": "https://developer.api.autodesk.com/project/v1/hubs"
}
},
"data": [
{
"type": "hubs",
"id": "a.YnV**OBSCURED**NjU",
"attributes": {
"name": "**OBSCURED**",
"extension": {
"type": "hubs:autodesk.core:Hub",
"version": "1.0",
"schema": {
"href": "https://developer.api.autodesk.com/schema/v1/versions/hubs:autodesk.core:Hub-1.0"
},
"data": {}
},
"region": "US"
},
"links": {
"self": {
"href": "https://developer.api.autodesk.com/project/v1/hubs/a.YnVz**OBSCURED**uNjU"
}
},
"relationships": {
"projects": {
"links": {
"related": {
"href": "https://developer.api.autodesk.com/project/v1/hubs/a.YnVza**OBSCURED**uNjU/projects"
}
}
}
}
},
{
"type": "hubs",
"id": "a.YnVz**OBSCURED**ltNQ",
"attributes": {
"name": "shane",
"extension": {
"type": "hubs:autodesk.core:Hub",
"version": "1.0",
"schema": {
"href": "https://developer.api.autodesk.com/schema/v1/versions/hubs:autodesk.core:Hub-1.0"
},
"data": {}
},
"region": "US"
},
"links": {
"self": {
"href": "https://developer.api.autodesk.com/project/v1/hubs/a.YnVz**OBSCURED**tNQ"
}
},
"relationships": {
"projects": {
"links": {
"related": {
"href": "https://developer.api.autodesk.com/project/v1/hubs/a.YnVz**OBSCURED**ltNQ/projects"
}
}
}
}
}
],
"meta": {
"warnings": [
{
"Id": null,
"HttpStatusCode": "403",
"ErrorCode": "BIM360DM_ERROR",
"Title": "Unable to get hubs from BIM360DM US.",
"Detail": "You don't have permission to access this API",
"AboutLink": null,
"Source": [],
"meta": []
},
{
"Id": null,
"HttpStatusCode": "403",
"ErrorCode": "BIM360DM_ERROR",
"Title": "Unable to get hubs from BIM360DM EMEA.",
"Detail": "You don't have permission to access this API",
"AboutLink": null,
"Source": [],
"meta": []
}
]
}
}
Here's what I see in my docs.b360.autodesk.com portal
Am I understanding this right, that hubs are “accounts” in the above screenshot? And projects would be projects listed in those accounts/hubs?
In order to see your BIM360 accounts listed among the hubs, you do have to provision the access for every single Forge app.
And yes, the BIM360 "accounts" would show up as individual "hubs" (starting with b.) in the response of the GET hubs call.
I have a JSON API endpoint for retrieving the user. This resource will also be used to get the permissions of the user, for showing or hiding specific elements in our front end application.
The resource looks like this:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"jsonapi": {
"version": "1.0"
},
"meta": {
"content-type": "application/vnd.api+json"
},
"links": {
"self": "/users/some-uuid"
},
"data": {
"type": "users",
"id": "some-uuid",
"attributes": {
"email": "some-email#example.com",
"permissions": [
"view-all-users",
"view-all-shifts"
]
},
"relationships": {
"roles": {
"data": [
{
"type": "role",
"id": "some-role-uuid"
}
]
}
}
}
}
The permissions attribute holds the slugs for the permissions that the user has.
If this attribute was not present the front end application would have to include the resources roles and roles.permissions to be able to get to the user's permissions. That response would look like the following:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"jsonapi": {
"version": "1.0"
},
"meta": {
"content-type": "application/vnd.api+json"
},
"links": {
"self": "/users/some-uuid"
},
"data": {
"type": "users",
"id": "some-uuid",
"attributes": {
"email": "some-email#example.com",
"permissions": [
"view-all-posts",
"edit-all-posts"
]
},
"relationships": {
"roles": {
"data": [
{
"type": "role",
"id": "some-role-uuid"
}
]
}
},
"included": [
{
"type": "roles",
"id": "some-role-uuid",
"attributes": {
"name": "Editor"
},
"relationships": {
"permissions": {
"data": [
{
"type": "permission",
"id": "some-permission-uuid"
},
{
"type": "permission",
"id": "some-permission-uuid-2"
}
]
}
}
},
{
"type": "permissions",
"id": "some-permission-uuid",
"attributes": {
"slug": "view-all-posts"
}
},
{
"type": "permissions",
"id": "some-permission-uuid",
"attributes": {
"slug": "edit-all-posts"
}
}
]
}
}
In this case the front end has to do a lot of processing just to get to the permission slugs. My question here is: Is it bad to have a short hand attribute permissions on the user resource like the above example, or should the front end always get to the slugs through the relationships?
Note: In the future we will have an admin interface where the user can manage users, roles and permissions. That is why the roles and permissions are available as seperate entities.
Client apps can easily merge all the permissions from roles into one key/array themselves and work from there. This way you'll keep the principles of JSON API in tact and give the client apps the freedom to work with permissions as they prefer.
I'm trying to create a template which does multiple vm's in Azure and then encrypts the disks, I've managed to get it working with 2 vm's about 3 hours ago, however when I do three of more vm's in the loop I get the following error:
"code": "DeploymentFailed",
"message": "At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/arm-debug for usage details.",
"details": [
{
"code": "Conflict",
"message": "{\r\n \"error\": {\r\n \"code\": \"DeploymentActive\",\r\n \"message\": \"Unable to edit or replace deployment 'updatevm': previous deployment
from '12/29/2018 1:11:34 AM' is still active (expiration time is '1/5/2019 1:11:34 AM'). Please see https://aka.ms/arm-deploy for usage details.\"\r\n }\r\n}"
}
]
}
]
Is there a way of putting a pause so it waits for the updatevm extension to complete?
The section of code I have for the encryption is:
{
"name": "[concat(parameters('VMNames'),copyIndex(),'UpdateEncryptionSettings')]",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2015-01-01",
"dependsOn": [
"[concat('Microsoft.Compute/virtualMachines/', parameters('VMNames'),copyIndex(1))]"
],
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[concat(parameters('_artifactsLocation'),'/nestedtemplates/encryptVm.json',parameters('_artifactsLocationSasToken'))]",
"contentVersion": "1.0.0.0"
},
"parameters": {
"vmName": {
"value": "[concat(parameters('VMNames'), copyIndex(1))]"
},
"aadClientID": {
"value": "[parameters('aadClientID')]"
},
"aadClientSecret": {
"value": "[parameters('aadClientSecret')]"
},
"keyVaultName": {
"value": "[parameters('keyVaultName')]"
},
"keyVaultResourceGroup": {
"value": "[parameters('keyVaultResourceGroup')]"
},
"useExistingKek": {
"value": "[parameters('useExistingKek')]"
},
"keyEncryptionKeyURL": {
"value": "[parameters('keyEncryptionKeyURL')]"
},
"_artifactsLocation": {
"value": "[parameters('_artifactsLocation')]"
},
"_artifactsLocationSasToken": {
"value": "[parameters('_artifactsLocationSasToken')]"
}
}
}
},
make it depend on the previous extension, since you do not provide the exact code, its something like:
"dependsOn": [
"updatevm"
}
this didnt exactly work due to how the templates were structured, the answer was to use serial copy mode to only create one copy at a timeю
"copy": {
"name": "storagecopy",
"count": 4,
"mode": "serial",
"batchSize": 1
}
https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-multiple#resource-iteration
I have created the ARM template for azure web app. I need to publish the ARM template to azure market place. I have used the azure publish portal https://publish.windowsazure.com/workspace/multi-resource-solutions for publishing the ARM template.
To on-board the ARM template to azure market place zip file must contain a mainTemplate.json and createUiDefinition.json. I found some samples for createUiDefination.json file in https://github.com/Azure/azure-quickstart-templates but all the createUiDefination.json is for VM. I am unable to find the samples or tutorials for createUiDefination.json for Azure web app.
I need to validate the azure web app site name is already exists or not. Also need to create or use the app service plan.
Is there is any tutorial or sample for creating createUiDefination.json for azure web app?
I need to validate the azure web app site name is already exists or
not.
This is not possible, you need to add to the site name a unique String, to ensure that the site name is globally unique.
For example you could use in your ARM template this function: uniqueString()
Similar question was answered by a Microsoft employee.
Also need to create or use the app service plan.
Add an App Service Plan to your Azure Resource Manager Template. For example like this:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"hostingPlanName": {
"type": "string",
"minLength": 1
},
"skuName": {
"type": "string",
"defaultValue": "F1",
"allowedValues": [
"F1",
"D1",
"B1",
"B2",
"B3",
"S1",
"S2",
"S3",
"P1",
"P2",
"P3",
"P4"
],
"metadata": {
"description": "Describes plan's pricing tier and capacity. Check details at https://azure.microsoft.com/en-us/pricing/details/app-service/"
}
},
"skuCapacity": {
"type": "int",
"defaultValue": 1,
"minValue": 1,
"metadata": {
"description": "Describes plan's instance count"
}
}
},
"variables": {
"webSiteName": "[concat('webSite', uniqueString(resourceGroup().id))]"
},
"resources": [
{
"apiVersion": "2015-08-01",
"name": "[parameters('hostingPlanName')]",
"type": "Microsoft.Web/serverfarms",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "HostingPlan"
},
"sku": {
"name": "[parameters('skuName')]",
"capacity": "[parameters('skuCapacity')]"
},
"properties": {
"name": "[parameters('hostingPlanName')]"
}
},
{
"apiVersion": "2015-08-01",
"name": "[variables('webSiteName')]",
"type": "Microsoft.Web/sites",
"location": "[resourceGroup().location]",
"tags": {
"[concat('hidden-related:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]": "Resource",
"displayName": "Website"
},
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]"
],
"properties": {
"name": "[variables('webSiteName')]",
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('hostingPlanName'))]"
}
}
]
}
Validating an app service's name is possible in createUiDefinition.json.
The crux of it is the Microsoft.Solutions.ArmApiControl, which can be used to call ARM apis as part of the validation of a text box. Here is an example:
{
"$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#",
"handler": "Microsoft.Azure.CreateUIDef",
"version": "0.1.2-preview",
"parameters": {
"basics": [
{}
],
"steps": [
{
"name": "domain",
"label": "Domain Names",
"elements": [
{
"name": "domainInfo",
"type": "Microsoft.Common.InfoBox",
"visible": true,
"options": {
"icon": "Info",
"text": "Pick the domain name that you want to use for your app."
}
},
{
"name": "appServiceAvailabilityApi",
"type": "Microsoft.Solutions.ArmApiControl",
"request": {
"method": "POST",
"path": "[concat(subscription().id, '/providers/Microsoft.Web/checknameavailability?api-version=2021-02-01')]",
"body": "[parse(concat('{\"name\":\"', concat('', steps('domain').domainName), '\", \"type\": \"Microsoft.Web/sites\"}'))]"
}
},
{
"name": "domainName",
"type": "Microsoft.Common.TextBox",
"label": "Domain Name Word",
"toolTip": "The name of your app service",
"placeholder": "yourcompanyname",
"constraints": {
"validations": [
{
"regex": "^[a-zA-Z0-9]{4,30}$",
"message": "Alphanumeric, between 4 and 30 characters."
},
{
"isValid": "[not(equals(steps('domain').appServiceAvailabilityApi.nameAvailable, false))]",
"message": "[concat('Error with the url: ', steps('domain').domainName, '. Reason: ', steps('domain').appServiceAvailabilityApi.reason)]"
},
{
"isValid": "[greater(length(steps('domain').domainName), 4)]",
"message": "The unique domain suffix should be longer than 4 characters."
},
{
"isValid": "[less(length(steps('domain').domainName), 30)]",
"message": "The unique domain suffix should be shorter than 30 characters."
}
]
}
},
{
"name": "section1",
"type": "Microsoft.Common.Section",
"label": "URLs to be created:",
"elements": [
{
"name": "domainExamplePortal",
"type": "Microsoft.Common.TextBlock",
"visible": true,
"options": {
"text": "[concat('https://', steps('domain').domainName, '.azurewebsites.net - The main app service URL')]"
}
}
],
"visible": true
}
]
}
],
"outputs": {
"desiredDomainName": "[steps('domain').domainName]"
}
}
}
You can paste that in to the createUiDefinition.json sandbox azure provides to test it out.
To look at how to do an Azure App Service instead of a VM, look in to documentation like this and use it as the maintemplate.json in the app.zip package.
Is there a way to retrive all the users/groups having access to a collaboration : BOX API?
The API defines a collaboration as an access control list. It's an object that grants a single user (or group) permissions to access a folder, so you can't really have access to a collaboration.
It sounds more like you're asking how you can get a list of all the collaborations on a folder, which you can do with:
GET /folders/{id}/collaborations
which will return something like:
{
"total_count": 1,
"entries": [
{
"type": "collaboration",
"id": "14176246",
"created_by": {
"type": "user",
"id": "4276790",
"name": "David Lee",
"login": "david#box.com"
},
"created_at": "2011-11-29T12:56:35-08:00",
"modified_at": "2012-09-11T15:12:32-07:00",
"expires_at": null,
"status": "accepted",
"accessible_by": {
"type": "user",
"id": "755492",
"name": "Simon Tan",
"login": "simon#box.net"
},
"role": "editor",
"acknowledged_at": "2011-11-29T12:59:40-08:00",
"item": null
}
]
}
https://developers.box.com/docs/#folders-view-a-folders-collaborations