Using D2L Rest API Sending following block for creating topic inside already existing forum:
URL: POST /d2l/api/le/(D2LVERSION: version)/(D2LID: orgUnitId)/discussions/forums/(D2LID: forumId)/topics/
{
"Name" : "Test Forum API",
"Description" : {
"Text" : "",
"Html" : "Test"
},
"AllowAnonymousPosts" : true,
"StartDate" : null,
"EndDate" : null,
"IsHidden" : false,
"UnlockStartDate" : null,
"UnlockEndDate" : null,
"RequiresApproval" : false,
"ScoreOutOf" : null,
"IsAutoScore" : true,
"IncludeNonScoredValues" : true,
"ScoringType" : null,
"IsLocked" : false,
"MustPostToParticipate" : true
}
Getting error:
INFO: Redirect requested but followRedirects is disabled
Status Code 302 Object moved to /d2l/error/404
Post and Redirect don't work together. Often environments (a loadbalancer or other network component) will automatically redirect all http operations to https. Then your client library won't actually follow the 302.
You probably want to use https if it is enabled, and you can make the D2L Libraries produce https urls.
(You may also want to have your client http library you are using follow redirects though because there are scenarios where the GET operations might get redirected)
Note that the CreateTopicData structure that you must pass the API to create a new topic does not use a RichText composite structure for the Description property on input. Instead, you must use a RichTextInput composite structure, which is slightly different:
{
"Name" : "Test Forum API",
"Description" : {
"Content" : "Test",
"Type": "HTML"
},
...
}
The API will pass back a RichText structure on output, however.
Using our test service, with a POST to an URL like this (assuming an org ID of 8083, and a forum ID of 4174)
https://myLMShost.edu/d2l/api/le/1.0/8083/discussions/forums/4174/topics/
we passed in a JSON structure that looks like this:
{'AllowAnonymousPosts': True,
'Description': {'Content': 'test', 'Type': 'HTML'},
'EndDate': None,
'IncludeNonScoredValues': False,
'IsAutoScore': True,
'IsHidden': False,
'IsLocked': False,
'MustPostToParticipate': True,
'Name': 'Test Forum API',
'RequiresApproval': False,
'ScoreOutOf': None,
'ScoringType': None,
'StartDate': None,
'UnlockEndDate': None,
'UnlockStartDate': None}
And our test service returned the new topic post, like this:
{'AllowAnonymousPosts': True,
'Description': {'Html': 'test', 'Text': ''},
'EndDate': None,
'ForumId': 4174,
'IncludeNonScoredValues': False,
'IsAutoScore': True,
'IsHidden': False,
'IsLocked': False,
'MustPostToParticipate': True,
'Name': 'Test Forum API',
'PinnedPostCount': 0,
'RatingsCount': 0,
'RatingsSum': 0,
'RequiresApproval': False,
'ScoreOutOf': None,
'ScoredCount': 0,
'ScoringType': None,
'StartDate': None,
'TopicId': 88569,
'UnapprovedPostCount': 0,
'UnlockEndDate': None,
'UnlockStartDate': None}
Related
I am trying to use Dart alongside a api for a school system i have reverse engineered and I'm trying to implement it in a library, when using dio to send a request it is converted to a map (the output of the api is json i think) but it is nested and I’m not sure on how to work with them.
This is the code i have written
class StudentClient{
String sessionID;
StudentClient(this.sessionID);
Future<Map> basicInfo() async {
var dio = Dio();
dio.options.headers["Authorization"] = "Basic $sessionID";
Response loginRequest = await dio.get(
'https://www.classcharts.com/apiv2student/ping',
options: Options(contentType: Headers.formUrlEncodedContentType),
);
assert(loginRequest.data is Map);
Map<dynamic, dynamic> request = loginRequest.data;
return request;
}
and the (censored) output
{success: 1, data: {user: {id: 696969, name: Foo Bar, first_name: Foo, last_name: Bar, avatar_url: https://asdsdsdsd/26.0.0/img/faces/default.png, display_behaviour: true, display_parent_behaviour: false, display_homework: true, display_rewards: true, display_detentions: true, display_report_cards: true, display_classes: false, display_announcements: true, display_attendance: false, display_attendance_type: none, display_attendance_percentage: true, display_activity: true, display_mental_health: false, display_mental_health_no_tracker: false, display_timetable: true, is_disabled: false, display_two_way_communications: true, display_absences: false, can_upload_attachments: false, display_event_badges: false, display_avatars: false, display_concern_submission: false, display_custom_fields: false, pupil_concerns_help_text: , allow_pupils_add_timetable_notes: false, detention_alias_plural_uc: Detentions, announcements_count: 0, messages_count: 0, pusher_channel_name: Pupil_dfghfghhghfghfghfghfghfghfghfghfghfgh, has_birthday: true, has_new_survey: false, survey_id: null}}, meta: {version: 26.0.0}}
Any help would be appreciated!
I need to just get the twin of a device using the Azure device sdk for node.js.
I did used the Client clode as below:-
import { Client } from 'azure-iot-device';
import { Mqtt } from 'azure-iot-device-mqtt';
await client.setOptions(options);
await client.open();
const twin = await client.getTwin();
The issue is the twin returned doesn't have the device twin fields but other fields like below:-
{
_events: [Object: null prototype] { newListener: [Function: bound ] },
_eventsCount: 1,
_maxListeners: undefined,
_transport: Mqtt {
_events: [Object: null prototype] {
error: [Function],
connected: [Function],
disconnect: [Array],
message: [Function],
twinDesiredPropertiesUpdate: [Function: bound ]
},
_eventsCount: 5,
_maxListeners: undefined,
_mid: '',
_firstConnection: false,
_authenticationProvider: X509AuthenticationProvider { type: 0, _credentials: [Object] },
_mqtt: MqttBase {
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
mqttProvider: [Object],
_onTheWirePublishes: [OnTheWireMessageContainer],
_fsm: [constructor],
_options: [Object],
_config: [Object],
_mqttClient: [MqttClient],
[Symbol(kCapture)]: false
},
_twinClient: MqttTwinClient {
_events: [Object: null prototype],
_eventsCount: 1,
_maxListeners: undefined,
_pendingTwinRequests: {},
_mqtt: [MqttBase],
_topicFsm: [BehavioralFsm],
_responseTopic: [Object],
_desiredPropertiesUpdatesTopic: [Object],
[Symbol(kCapture)]: false
},
_fsm: constructor {
initialState: 'disconnected',
states: [Object],
eventListeners: [Object],
namespace: 'fsm.2',
useSafeEmit: false,
hierarchy: {},
pendingDelegations: {},
_stamped: true,
inputQueue: [],
targetReplayState: 'connected',
state: 'connected',
priorState: 'connecting',
priorAction: 'connected.getTwin',
currentAction: '',
currentActionArgs: undefined,
inExitHandler: false
},
_topicTelemetryPublish: 'devices/amidha/messages/events/',
_topics: { method: [Object], message: [Object] },
_userAgentString: 'azure-iot-device/1.17.1 (node v12.18.0; Ubuntu 18.04; x64)',
[Symbol(kCapture)]: false
},
_retryPolicy: ExponentialBackOffWithJitter {
_errorFilter: DefaultErrorFilter {
ArgumentError: false,
ArgumentOutOfRangeError: false,
DeviceMaximumQueueDepthExceededError: false,
DeviceNotFoundError: false,
FormatError: false,
UnauthorizedError: false,
NotImplementedError: false,
NotConnectedError: true,
IotHubQuotaExceededError: false,
MessageTooLargeError: false,
InternalServerError: true,
ServiceUnavailableError: true,
IotHubNotFoundError: false,
IoTHubSuspendedError: false,
JobNotFoundError: false,
TooManyDevicesError: false,
ThrottlingError: true,
DeviceAlreadyExistsError: false,
DeviceMessageLockLostError: false,
InvalidEtagError: false,
InvalidOperationError: false,
PreconditionFailedError: false,
TimeoutError: true,
BadDeviceResponseError: false,
GatewayTimeoutError: false,
DeviceTimeoutError: false,
TwinRequestError: false
},
immediateFirstRetry: true,
normalParameters: ExponentialBackoffWithJitterParameters {
c: 100,
cMin: 100,
cMax: 10000,
ju: 0.25,
jd: 0.5
},
throttledParameters: ExponentialBackoffWithJitterParameters {
c: 5000,
cMin: 10000,
cMax: 60000,
ju: 0.25,
jd: 0.5
}
},
_maxOperationTimeout: 240000,
desiredPropertiesUpdatesEnabled: false,
properties: {
reported: { update: [Function: update], '$version': 1 },
desired: { '$version': 1 }
},
[Symbol(kCapture)]: false
}
I don't want to listen the twin change events that I can do easily by using above object using twin.on. I need to just get the current twin of the device. Is it possible?
After reading your comments it seems you are expecting some values to be there that just aren't available to the device (when you use the device SDK). The device can read and receive updates on desired properties and read/write to reported properties, the rest is unavailable.
In a comment, you mentioned you want to find the deviceScope in the twin, but that isn't available to the device SDK. You would need the service SDK for that.
When you print the result of const twin = await client.getTwin();, what you see is an object with helper methods to subscribe to desired property changes and patching new reported properties. Like Mark stated in his question, the properties of your twin are included in this object as well.
The current twin is in the JSON you posted. See the section properties:
properties: {
reported: { update: [Function: update], '$version': 1 },
desired: { '$version': 1 }
},
The twin you posted is empty.
Above is not feasible to get the whole twin not just the desired and reported properties from device IoT sdk and that has been confirmed by Azure too. Check here.
The link also has a solution to assign parent child relationship between the leaf and the edge device.
I upload an image to slack using https://slack.com/api/files.upload
not specifying channels field in the request.
I can see the image in the web interface. What the api call should be to share the uploaded image in some channel at some point in the future?
I tried to upload an image and response from slack was:
{
"ok": true,
"file": {
"id": "FHJ9QTX1V",
"created": 1554115093,
"timestamp": 1554115093,
"name": "scaled_IMG-20190324-WA0002.jpg",
"title": "scaled IMG-20190324-WA0002",
"mimetype": "image/jpeg",
"filetype": "jpg",
"pretty_type": "JPEG",
"user": "UGRR6FCF7",
"editable": false,
"size": 217356,
"mode": "hosted",
"is_external": false,
"external_type": "",
"is_public": false,
"public_url_shared": false,
"display_as_bot": false,
"username": "",
"url_private": "https://files.slack.com/files-pri/TGQU3SCHF-FHJ9QTX1V/scaled_img-20190324-wa0002.jpg",
"url_private_download": "https://files.slack.com/files-pri/TGQU3SCHF-FHJ9QTX1V/download/scaled_img-20190324-wa0002.jpg",
"thumb_64": "https://files.slack.com/files-tmb/TGQU3SCHF-FHJ9QTX1V-fa34003fce/scaled_img-20190324-wa0002_64.jpg",
"thumb_80": "https://files.slack.com/files-tmb/TGQU3SCHF-FHJ9QTX1V-fa34003fce/scaled_img-20190324-wa0002_80.jpg",
"thumb_360": "https://files.slack.com/files-tmb/TGQU3SCHF-FHJ9QTX1V-fa34003fce/scaled_img-20190324-wa0002_360.jpg",
"thumb_360_w": 360,
"thumb_360_h": 270,
"thumb_480": "https://files.slack.com/files-tmb/TGQU3SCHF-FHJ9QTX1V-fa34003fce/scaled_img-20190324-wa0002_480.jpg",
"thumb_480_w": 480,
"thumb_480_h": 360,
"thumb_160": "https://files.slack.com/files-tmb/TGQU3SCHF-FHJ9QTX1V-fa34003fce/scaled_img-20190324-wa0002_160.jpg",
"thumb_720": "https://files.slack.com/files-tmb/TGQU3SCHF-FHJ9QTX1V-fa34003fce/scaled_img-20190324-wa0002_720.jpg",
"thumb_720_w": 720,
"thumb_720_h": 540,
"thumb_800": "https://files.slack.com/files-tmb/TGQU3SCHF-FHJ9QTX1V-fa34003fce/scaled_img-20190324-wa0002_800.jpg",
"thumb_800_w": 800,
"thumb_800_h": 600,
"thumb_960": "https://files.slack.com/files-tmb/TGQU3SCHF-FHJ9QTX1V-fa34003fce/scaled_img-20190324-wa0002_960.jpg",
"thumb_960_w": 960,
"thumb_960_h": 720,
"thumb_1024": "https://files.slack.com/files-tmb/TGQU3SCHF-FHJ9QTX1V-fa34003fce/scaled_img-20190324-wa0002_1024.jpg",
"thumb_1024_w": 1024,
"thumb_1024_h": 768,
"image_exif_rotation": 1,
"original_w": 1040,
"original_h": 780,
"permalink": "https://autolainen.slack.com/files/UGRR6FCF7/FHJ9QTX1V/scaled_img-20190324-wa0002.jpg",
"permalink_public": "https://slack-files.com/TGQU3SCHF-FHJ9QTX1V-3366c52c9c",
"comments_count": 0,
"is_starred": false,
"shares": {},
"channels": [],
"groups": [],
"ims": [],
"has_rich_preview": false
}
}
And then tried to share the image using /api/chat.postMessage:
{
"channel": "CH68ZSHFA",
"text": "test",
"blocks": [
{
"type": "section",
"text": {
"type": "plain_text",
"text": "test"
}
},
{
"type": "image",
"image_url": "https://autolainen.slack.com/files/UGRR6FCF7/FHJ9QTX1V/scaled_img-20190324-wa0002.jpg",
"alt_text": "attachment"
}
],
"as_user": false,
"username": "Client name"
}
I used url from url_private, url_private_download, permalink, permalink_public fields of the file description but got the same response:
{
"ok": false,
"error": "invalid_blocks",
"response_metadata": {
"messages": [
"[ERROR] downloading image failed [json-pointer:/1/image_url]"
]
}
}
Unfortunately there is no official API method to share a file on your workspace after it has been uppladed. So if you don't include the channel(s) in your initial files.upload request there is no official way to share that file later on.
But there is an unofficial API method called files.share, which has that very functionality. It works perfectly, but you will need a legacy token to use it, so this might not be a solution for you.
Another way to use an image on Slack is to include it in a message (as you are trying in your code example). Technically speaking that is not the same as sharing a file on Slack, since it only works for images and provide limited functionality for users.
It will work though, but only if your image URL is public, because chat.postMessage only works with public URLs to images and files on Slack are private by default (which means you need to provide authorization in any request to access that file from outside of Slack).
To get a public URL for your file you can call the API method files.sharedPublicURL after you uploaded it. You will get a public URL as response, which you can then use to include that image in your message.
I'm using the docusign rest gem to create and send DocuSign envelopes. But I'm having problems having custom fields I have set on my template to show up. I have 2 signers on my template, that have common fields to both of them so I had to set up each custom field twice since DocuSign doesn't allow for shared fields it seems.
This is the API call I'm doing when setting up my envelope, as per the DocuSign API docs:
custom_fields = {
textCustomFields: [
{
name: "host_address",
value: "Testing Host Address",
required: "true",
show: "true",
},
{
name: "host_civil_status",
value: "Host Civil Status",
required: "true",
show: "true",
},
{
name: "host_id_number",
value: "123HOSTID",
required: "true",
show: "true",
},
{
name: "host_tax_number",
value: "123HOSTTAX",
required: "true",
show: "true",
},
{
name: "nomad_address",
value: "Testing Nomad Address",
required: "true",
show: "true",
},
{
name: "nomad_civil_status",
value: "Nomad Civil Status",
required: "true",
show: "true",
},
{
name: "nomad_id_number",
value: "123NOMADID",
required: "true",
show: "true",
},
{
name: "nomad_tax_number",
value: "123NOMADTAX",
required: "true",
show: "true",
},
{
name: "property_addres",
value: booking.listing.property.full_address,
required: "true",
show: "true",
},
{
name: "property_address",
value: booking.listing.property.full_address,
required: "true",
show: "true",
},
{
name: "property_deposit",
value: (booking.deposit * booking.price).to_s,
required: "true",
show: "true",
},
{
name: "property_description",
value: "Property Description",
required: "true",
show: "true",
},
{
name: "property_start_date",
value: booking.start_date.strftime("%d/%m/%Y"),
required: "true",
show: "true",
},
{
name: "property_end_date",
value: booking.end_date.strftime("%d/%m/%Y"),
required: "true",
show: "true",
},
{
name: "property_stay_length",
value: distance_of_time_in_words(booking.start_date, booking.end_date),
required: "true",
show: "true",
},
{
name: "property_montly_rent",
value: booking.price.to_s,
required: "true",
show: "true",
},
]
}
#envelope = client.create_envelope_from_template(
status: 'sent',
email: {
subject: "The test email subject envelope",
body: "Envelope body content here"
},
template_id: "77xxxxxxxxxxxxxxxxxxxxxxxxxx",
signers: [
{
embedded: true,
name: booking.listing.user.name,
email: booking.listing.user.email,
role_name: 'Host',
},
{
embedded: true,
name: booking.user.name,
email: booking.user.email,
role_name: 'Nomad',
}
],
custom_fields: custom_fields
)
Unfortunately, there are two different objects in the DocuSign system that essentially have the same name of "custom field." You're using the wrong one.
The two objects are:
1. Metadata custom data fields
These objects come in two flavors: textCustomFields and listCustomFields.
These objects can be set at the account level to require that every every (or some) envelopes sent include additional metadata at the envelope level.
The objects can also be set at the envelope level programmatically.
These "custom fields" are not visible to the signer. They are not fields (tabs) visible on the documents. They are associated with the envelope as additional metadata. These are the type of custom fields that your code example is using.
Adding/listing the custom fields set at the account level: docs.
Adding a custom metadata field to an envelope: use the customFields attribute of the envelope definition.
2. Customized tabs (fields)
A customized tab (called a 'field' in the DocuSign web tool) is a version of a DocuSign tab (text tab, sign here tab, date tab, etc) that has been customized. Then, later, instead of re-customizing another instance of the tab, the customized tab can be re-used.
This is what you want to use.
To use the API to list them, update them, etc: docs.
To use them in a document
Disclaimer: I'm reading the docs, I have not tried this. Please add a comment after you try it:
You will need the customTabId for the customized tab. Use the CustomTabs::list API call.
You also need to know the base tab type: Text, etc. Use the type value which is also returned by the CustomTabs::list API call.
Then, when creating your document, for each recipient who gets a customized, tab, create the tab as usual, but include the customTabId as part of the tab's definition. Eg, see the text tab object definition in Envelopes::create API call.
I am using an API call which returns a JSON response. I want to access the data inside the response so I can create some nice display cards showing the info and pictures. Here is a snippet from the response, the response property is populated with about 20 objects I'll include just two for brevity:
{
"success": true,
"message": "",
"result": [
{
"MarketCurrency": "LTC",
"BaseCurrency": "BTC",
"MarketCurrencyLong": "Litecoin",
"BaseCurrencyLong": "Bitcoin",
"MinTradeSize": 1e-8,
"MarketName": "BTC-LTC",
"IsActive": true,
"Created": "2014-02-13T00:00:00",
"Notice": null,
"IsSponsored": null,
"LogoUrl": "https://i.imgur.com/R29q3dD.png"
},
{
"MarketCurrency": "DOGE",
"BaseCurrency": "BTC",
"MarketCurrencyLong": "Dogecoin",
"BaseCurrencyLong": "Bitcoin",
"MinTradeSize": 1e-8,
"MarketName": "BTC-DOGE",
"IsActive": true,
"Created": "2014-02-13T00:00:00",
"Notice": null,
"IsSponsored": null,
"LogoUrl": "https://i.imgur.com/e1RS4Hn.png"
},
In my Rails controller I'm using JSON.parse and I'm trying to turn it into an object with the Open struct option:
#markets = JSON.parse(markets.to_json, object_class: OpenStruct)
In my view I'll do this <%=#markets.class%> and it shows Array and not object. So I try this <%=#markets.size%> and it shows 1. If I do <%=#markets[0]['success']%> I would expect it to return true but it returns 'success'. So, I'm not understanding why the ostruct library isn't working like I would expect or how I can get to the objects stored in the result array. Any help is greatly appreciated!
You already have a JSON response, isn't needed to use to_json again, try just parsing that object, and then use the dot . to access its fields, as an OpenStruct object now then you can access them as methods:
require 'json'
a = '{
"success": true,
"message": "",
"result": [{
"MarketCurrency": "LTC",
"BaseCurrency": "BTC",
"MarketCurrencyLong": "Litecoin",
"BaseCurrencyLong": "Bitcoin",
"MinTradeSize": 1e-8,
"MarketName": "BTC-LTC",
"IsActive": true,
"Created": "2014-02-13T00:00:00",
"Notice": null,
"IsSponsored": null,
"LogoUrl": "https://i.imgur.com/R29q3dD.png"
}, {
"MarketCurrency": "DOGE",
"BaseCurrency": "BTC",
"MarketCurrencyLong": "Dogecoin",
"BaseCurrencyLong": "Bitcoin",
"MinTradeSize": 1e-8,
"MarketName": "BTC-DOGE",
"IsActive": true,
"Created": "2014-02-13T00:00:00",
"Notice": null,
"IsSponsored": null,
"LogoUrl": "https://i.imgur.com/e1RS4Hn.png"
}]
}'
b = JSON.parse(a, object_class: OpenStruct)
p b.success
# => true
After much debugging and some assistance, I was able to get it to work. The response from the API call was an array with one item. The item was a long string of the entire dataset.
In order to get the expected behavior of "true" when calling #markets.success, I first had to
raw_markets = JSON.parse(markets.to_json)
followed by
#markets = raw_markets.map do |market|
JSON.parse(market, object_class: OpenStruct)
Note: the variable markets holds the original api call:
markets = open('url-to-api')
After this I would get #markets.success = "true" and #markets.result[0] held the first result, #markets.result[1] held the second result, and so on.