@windward/integrations 0.0.8 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/components/ExternalIntegration/Driver/Lti1p1/ManageProvider.vue +9 -6
  2. package/components/ExternalIntegration/Driver/Lti1p1/ManageProviders.vue +6 -5
  3. package/components/ExternalIntegration/Driver/Lti1p3/ManageProvider.vue +445 -0
  4. package/components/ExternalIntegration/Driver/Lti1p3/ManageProviders.vue +259 -0
  5. package/components/ExternalIntegration/Driver/ManageLti1p3.vue +45 -0
  6. package/components/FileImport/Dropbox.vue +9 -0
  7. package/components/FileImport/FileImportMenu.vue +111 -0
  8. package/components/FileImport/GoogleDrive.vue +9 -0
  9. package/components/FileImport/Resourcespace.vue +202 -0
  10. package/components/Integration/Driver/ManageAtutor.vue +35 -12
  11. package/components/Integration/Driver/ManageResourcespace.vue +137 -0
  12. package/components/Integration/JobLog.vue +281 -0
  13. package/components/Integration/JobTable.vue +15 -1
  14. package/components/SecretField.vue +1 -0
  15. package/config/integration.config.js +2 -0
  16. package/helpers/Driver/Resourcespace.ts +15 -0
  17. package/i18n/en-US/components/external_integration/driver/lti1p3.ts +13 -0
  18. package/i18n/en-US/components/external_integration/index.ts +2 -1
  19. package/i18n/en-US/components/file_import/index.ts +5 -0
  20. package/i18n/en-US/components/file_import/resourcespace.ts +4 -0
  21. package/i18n/en-US/components/index.ts +2 -0
  22. package/i18n/en-US/components/integration/driver.ts +8 -0
  23. package/i18n/en-US/components/integration/index.ts +2 -0
  24. package/i18n/en-US/components/integration/job.ts +1 -0
  25. package/i18n/en-US/components/integration/job_log.ts +8 -0
  26. package/i18n/en-US/shared/error.ts +1 -0
  27. package/i18n/en-US/shared/file.ts +5 -0
  28. package/i18n/en-US/shared/index.ts +2 -0
  29. package/models/ExternalIntegration/{Lti1p1Provider.ts → LtiProvider.ts} +2 -2
  30. package/models/OrganizationIntegration.ts +5 -0
  31. package/models/RemoteFile.ts +12 -0
  32. package/package.json +1 -1
  33. package/pages/admin/importCourse.vue +8 -2
  34. package/pages/admin/vendors.vue +3 -2
  35. package/pages/course/externalIntegration/index.vue +4 -3
  36. package/plugin.js +84 -1
  37. package/test/Components/ExternalIntegration/Lti1p3/ManageProvider.spec.js +19 -0
  38. package/test/Components/ExternalIntegration/Lti1p3/ManageProviders.spec.js +19 -0
  39. package/test/Components/ExternalIntegration/ManageLti1p3.spec.js +19 -0
  40. package/test/Components/FileImport/Dropbox.spec.js +24 -0
  41. package/test/Components/FileImport/GoogleDrive.spec.js +24 -0
  42. package/test/Components/FileImport/Resourcespace.spec.js +24 -0
  43. package/test/Components/Integration/Driver/ManageAtutor.spec.js +22 -0
  44. package/test/Components/Integration/Driver/ManageResourcespace.spec.js +22 -0
  45. package/test/Components/Integration/JobLog.spec.js +22 -0
  46. package/test/Components/Integration/JobTable.spec.js +23 -0
  47. package/test/__mocks__/componentsMock.js +24 -0
  48. package/test/__mocks__/modelMock.js +1 -0
  49. package/test/mocks.js +12 -0
@@ -254,12 +254,12 @@
254
254
  <script>
255
255
  import _ from 'lodash'
256
256
  import { mapGetters } from 'vuex'
257
+ import ProviderTargetPicker from '../../ProviderTargetPicker.vue'
258
+ import LtiProvider from '../../../../models/ExternalIntegration/LtiProvider'
257
259
  import FormVue from '~/components/Form.vue'
258
260
  import Role from '~/models/Role'
259
261
  import Organization from '~/models/Organization'
260
262
  import Course from '~/models/Course'
261
- import ProviderTargetPicker from '../../ProviderTargetPicker.vue'
262
- import Lti1p1Provider from '../../../../models/ExternalIntegration/Lti1p1Provider'
263
263
 
264
264
  export default {
265
265
  name: 'ManageLti1p1ProviderDriver',
@@ -267,7 +267,7 @@ export default {
267
267
  extends: FormVue,
268
268
  props: {
269
269
  value: {
270
- type: [Lti1p1Provider, null],
270
+ type: [LtiProvider, null],
271
271
  required: false,
272
272
  default: null,
273
273
  },
@@ -286,6 +286,7 @@ export default {
286
286
  provider: {
287
287
  role_metadata: {},
288
288
  metadata: {},
289
+ version: '1.1',
289
290
  },
290
291
  rolePanel: false,
291
292
  roles: [],
@@ -313,9 +314,10 @@ export default {
313
314
  },
314
315
  created() {
315
316
  if (_.isEmpty(this.value)) {
316
- this.provider = new Lti1p1Provider({
317
+ this.provider = new LtiProvider({
317
318
  role_metadata: {},
318
319
  metadata: {},
320
+ version: '1.1',
319
321
  })
320
322
  } else {
321
323
  this.provider = _.cloneDeep(this.value)
@@ -360,7 +362,7 @@ export default {
360
362
  this.newMap.id = null
361
363
  },
362
364
  async save() {
363
- let provider = new Lti1p1Provider(this.provider).for(
365
+ let provider = new LtiProvider(this.provider).for(
364
366
  new Organization({ id: this.organization.id }),
365
367
  new Course({ id: this.course.id })
366
368
  )
@@ -384,9 +386,10 @@ export default {
384
386
 
385
387
  // Create a new provider to clear the form if the original value is empty aka new
386
388
  if (_.isEmpty(this.value)) {
387
- this.provider = new Lti1p1Provider({
389
+ this.provider = new LtiProvider({
388
390
  role_metadata: {},
389
391
  metadata: {},
392
+ version: '1.1',
390
393
  })
391
394
  }
392
395
  },
@@ -98,13 +98,13 @@
98
98
  <script>
99
99
  import _ from 'lodash'
100
100
  import { mapGetters } from 'vuex'
101
- import Course from '~/models/Course'
102
- import Organization from '~/models/Organization'
103
- import Dialog from '~/components/Dialog.vue'
104
- import Lti1p1Provider from '../../../../models/ExternalIntegration/Lti1p1Provider'
101
+ import LtiProvider from '../../../../models/ExternalIntegration/LtiProvider'
105
102
  import SecretField from '../../../SecretField.vue'
106
103
  import ProviderTargetViewer from '../../ProviderTargetViewer.vue'
107
104
  import ManageProvider from './ManageProvider.vue'
105
+ import Dialog from '~/components/Dialog.vue'
106
+ import Organization from '~/models/Organization'
107
+ import Course from '~/models/Course'
108
108
 
109
109
  export default {
110
110
  name: 'ManageLti1p1ProvidersDriver',
@@ -205,11 +205,12 @@ export default {
205
205
  this.loadProviders()
206
206
  },
207
207
  async loadProviders() {
208
- this.providers = await new Lti1p1Provider()
208
+ this.providers = await new LtiProvider()
209
209
  .for(
210
210
  new Organization({ id: this.organization.id }),
211
211
  new Course({ id: this.course.id })
212
212
  )
213
+ .where('version', '1.1')
213
214
  .get()
214
215
  },
215
216
  onConfirmDelete(provider) {
@@ -0,0 +1,445 @@
1
+ <template>
2
+ <div>
3
+ <div v-if="!render" class="integration-loading">
4
+ <v-progress-circular size="128" indeterminate />
5
+ </div>
6
+ <div v-if="render">
7
+ <v-form ref="form" v-model="formValid" @submit.prevent>
8
+ <v-row justify="center" align="center">
9
+ <v-col cols="12">
10
+ <ProviderTargetPicker
11
+ v-model="provider"
12
+ ></ProviderTargetPicker>
13
+
14
+ <v-text-field
15
+ id="target-url"
16
+ v-model="provider.target"
17
+ disabled
18
+ :label="
19
+ $t(
20
+ 'windward.integrations.components.external_integration.target_url'
21
+ )
22
+ "
23
+ :hint="
24
+ $t(
25
+ 'windward.integrations.components.external_integration.target_url'
26
+ )
27
+ "
28
+ :rules="validation.existsRules"
29
+ ></v-text-field>
30
+
31
+ <v-switch
32
+ v-model="provider.enabled"
33
+ :label="$t('shared.forms.enabled')"
34
+ />
35
+
36
+ <v-switch
37
+ v-model="provider.grade_sync"
38
+ :label="
39
+ $t(
40
+ 'windward.integrations.components.external_integration.send_grades'
41
+ )
42
+ "
43
+ />
44
+ <v-text-field
45
+ id="lti-keyset_url"
46
+ v-model="
47
+ provider.metadata.platform_public_keyset_url
48
+ "
49
+ :label="
50
+ $t(
51
+ 'windward.integrations.components.external_integration.driver.lti1p3.platform_public_keyset_url'
52
+ )
53
+ "
54
+ :hint="
55
+ $t(
56
+ 'windward.integrations.components.external_integration.driver.lti1p3.platform_public_keyset_url'
57
+ )
58
+ "
59
+ :rules="validation.existsRules"
60
+ ></v-text-field>
61
+
62
+ <v-text-field
63
+ id="lti-oidc-auth-endpoint"
64
+ v-model="
65
+ provider.metadata.platform_oidc_auth_endpoint
66
+ "
67
+ :placeholder="
68
+ $t(
69
+ 'windward.integrations.components.external_integration.driver.lti1p3.platform_oidc_auth_endpoint'
70
+ )
71
+ "
72
+ :label="
73
+ $t(
74
+ 'windward.integrations.components.external_integration.driver.lti1p3.platform_oidc_auth_endpoint'
75
+ )
76
+ "
77
+ :hint="
78
+ $t(
79
+ 'windward.integrations.components.external_integration.driver.lti1p3.platform_oidc_auth_endpoint'
80
+ )
81
+ "
82
+ :rules="validation.existsRules"
83
+ >
84
+ </v-text-field>
85
+ <v-text-field
86
+ id="lti-client-id"
87
+ v-model="provider.metadata.platform_client_id"
88
+ :placeholder="
89
+ $t(
90
+ 'windward.integrations.components.external_integration.driver.lti1p3.platform_client_id'
91
+ )
92
+ "
93
+ :label="
94
+ $t(
95
+ 'windward.integrations.components.external_integration.driver.lti1p3.platform_client_id'
96
+ )
97
+ "
98
+ :hint="
99
+ $t(
100
+ 'windward.integrations.components.external_integration.driver.lti1p3.platform_client_id'
101
+ )
102
+ "
103
+ >
104
+ </v-text-field>
105
+ <v-text-field
106
+ id="lti-deployment-id"
107
+ v-model="provider.metadata.platform_deployment_id"
108
+ :placeholder="
109
+ $t(
110
+ 'windward.integrations.components.external_integration.driver.lti1p3.platform_deployment_id'
111
+ )
112
+ "
113
+ :label="
114
+ $t(
115
+ 'windward.integrations.components.external_integration.driver.lti1p3.platform_deployment_id'
116
+ )
117
+ "
118
+ :hint="
119
+ $t(
120
+ 'windward.integrations.components.external_integration.driver.lti1p3.platform_deployment_id'
121
+ )
122
+ "
123
+ >
124
+ </v-text-field>
125
+ </v-col>
126
+ </v-row>
127
+ <v-row>
128
+ <v-col cols="12">
129
+ <v-expansion-panels v-model="rolePanel">
130
+ <v-expansion-panel>
131
+ <v-expansion-panel-header>
132
+ {{
133
+ $t(
134
+ 'windward.integrations.components.external_integration.role_map_panel'
135
+ )
136
+ }}
137
+ </v-expansion-panel-header>
138
+ <v-expansion-panel-content>
139
+ <p>
140
+ <strong>
141
+ {{
142
+ $t(
143
+ 'windward.integrations.components.external_integration.role_map_instructions'
144
+ )
145
+ }}
146
+ </strong>
147
+ </p>
148
+ <p>
149
+ {{
150
+ $t(
151
+ 'windward.integrations.components.external_integration.role_map_warning'
152
+ )
153
+ }}
154
+ </p>
155
+ <v-simple-table>
156
+ <template #default>
157
+ <thead>
158
+ <tr>
159
+ <th>
160
+ {{
161
+ $t(
162
+ 'windward.integrations.components.external_integration.role_in_host'
163
+ )
164
+ }}
165
+ </th>
166
+ <th>
167
+ {{
168
+ $t(
169
+ 'windward.integrations.components.external_integration.role_in_local'
170
+ )
171
+ }}
172
+ </th>
173
+ </tr>
174
+ </thead>
175
+ <tbody>
176
+ <tr
177
+ v-for="(
178
+ id, externalName
179
+ ) in provider.role_metadata"
180
+ :key="externalName"
181
+ >
182
+ <td>
183
+ {{
184
+ uppercase(
185
+ externalName
186
+ )
187
+ }}
188
+ </td>
189
+ <td>
190
+ <v-select
191
+ v-model="
192
+ provider
193
+ .role_metadata[
194
+ externalName
195
+ ]
196
+ "
197
+ :items="roles"
198
+ item-value="id"
199
+ item-text="name"
200
+ :label="
201
+ $t(
202
+ 'windward.integrations.components.external_integration.map_to_role'
203
+ )
204
+ "
205
+ ></v-select>
206
+ </td>
207
+ <td>
208
+ <v-btn
209
+ icon
210
+ @click="
211
+ deleteMapItem(
212
+ externalName
213
+ )
214
+ "
215
+ >
216
+ <v-icon>
217
+ mdi-delete
218
+ </v-icon>
219
+ <span
220
+ class="sr-only"
221
+ >
222
+ {{
223
+ $t(
224
+ 'windward.integrations.components.external_integration.delete_role_map_item'
225
+ )
226
+ }}
227
+ </span>
228
+ </v-btn>
229
+ </td>
230
+ </tr>
231
+ <tr>
232
+ <td>
233
+ <v-text-field
234
+ v-model="
235
+ newMap.name
236
+ "
237
+ :label="
238
+ $t(
239
+ 'windward.integrations.components.external_integration.remote_role'
240
+ )
241
+ "
242
+ ></v-text-field>
243
+ </td>
244
+ <td>
245
+ <v-select
246
+ v-model="newMap.id"
247
+ :items="roles"
248
+ :label="
249
+ $t(
250
+ 'windward.integrations.components.external_integration.map_to_role'
251
+ )
252
+ "
253
+ item-value="id"
254
+ item-text="name"
255
+ ></v-select>
256
+ </td>
257
+ <td>
258
+ <v-btn
259
+ icon
260
+ :disabled="
261
+ !newMap.id ||
262
+ !newMap.name
263
+ "
264
+ @click="addMapItem"
265
+ >
266
+ <v-icon>
267
+ mdi-plus
268
+ </v-icon>
269
+ <span
270
+ class="sr-only"
271
+ >
272
+ {{
273
+ $t(
274
+ 'windward.integrations.components.external_integration.add_role_map_item'
275
+ )
276
+ }}
277
+ </span>
278
+ </v-btn>
279
+ </td>
280
+ </tr>
281
+ </tbody>
282
+ </template>
283
+ </v-simple-table>
284
+ </v-expansion-panel-content>
285
+ </v-expansion-panel>
286
+ </v-expansion-panels>
287
+ </v-col>
288
+ </v-row>
289
+ </v-form>
290
+ </div>
291
+ </div>
292
+ </template>
293
+
294
+ <script>
295
+ import _ from 'lodash'
296
+ import { mapGetters } from 'vuex'
297
+ import ProviderTargetPicker from '../../ProviderTargetPicker.vue'
298
+ import LtiProvider from '../../../../models/ExternalIntegration/LtiProvider'
299
+
300
+ import FormVue from '~/components/Form.vue'
301
+ import Role from '~/models/Role'
302
+ import Organization from '~/models/Organization'
303
+ import Course from '~/models/Course'
304
+
305
+ export default {
306
+ name: 'ManageLti1p3ProviderDriver',
307
+ components: { ProviderTargetPicker },
308
+ extends: FormVue,
309
+ props: {
310
+ value: {
311
+ type: [LtiProvider, null],
312
+ required: false,
313
+ default: null,
314
+ },
315
+ },
316
+ emits: ['update:provider'],
317
+ data() {
318
+ return {
319
+ render: false,
320
+ provider: {
321
+ role_metadata: {},
322
+ metadata: {},
323
+ version: '1.3',
324
+ },
325
+ rolePanel: false,
326
+ roles: [],
327
+ newMap: {
328
+ name: null,
329
+ id: null,
330
+ },
331
+ }
332
+ },
333
+ async fetch() {
334
+ // Get available roles but filter out default high level roles since they'll be ignore anyways from the api
335
+ this.roles = await Role.whereIn('name,ne', [
336
+ 'admin',
337
+ 'editor',
338
+ 'instructor',
339
+ 'student',
340
+ ]).all()
341
+ },
342
+ computed: {
343
+ ...mapGetters({
344
+ organization: 'organization/get',
345
+ course: 'course/get',
346
+ contentTree: 'content/getTree',
347
+ }),
348
+ },
349
+ created() {
350
+ if (_.isEmpty(this.value)) {
351
+ this.provider = new LtiProvider({
352
+ role_metadata: {},
353
+ metadata: {},
354
+ version: '1.3',
355
+ })
356
+ } else {
357
+ this.provider = _.cloneDeep(this.value)
358
+ }
359
+ },
360
+ mounted() {
361
+ if (
362
+ !this.$PermissionService.userHasAccessTo(
363
+ 'plugin.windward.integrations.course.externalIntegration',
364
+ 'writable'
365
+ )
366
+ ) {
367
+ // Display an angry error that they can't view this driver
368
+ this.$dialog.error(this.$t('shared.error.description_401'), {
369
+ duration: null,
370
+ action: {
371
+ text: this.$t('shared.forms.close'),
372
+ onClick: (_e, toastObject) => {
373
+ toastObject.goAway(0)
374
+ },
375
+ },
376
+ })
377
+ // eslint-disable-next-line no-console
378
+ console.error('You do not have access to this provider!')
379
+
380
+ // Return so we don't even attempt loading
381
+ return false
382
+ }
383
+
384
+ this.render = true
385
+ },
386
+ methods: {
387
+ uppercase(value) {
388
+ return _.startCase(value)
389
+ },
390
+ deleteMapItem(key) {
391
+ this.$delete(this.provider.role_metadata, key)
392
+ },
393
+ addMapItem() {
394
+ this.provider.role_metadata[this.newMap.name] = this.newMap.id
395
+ this.newMap.name = null
396
+ this.newMap.id = null
397
+ },
398
+ async save() {
399
+ let provider = new LtiProvider(this.provider).for(
400
+ new Organization({ id: this.organization.id }),
401
+ new Course({ id: this.course.id })
402
+ )
403
+
404
+ try {
405
+ provider = await provider.save()
406
+ this.provider = provider
407
+
408
+ // Clone and delete the metadata since we don't need to share that around
409
+ // Also include the vendor going back for easier mapping
410
+ const providerEvent = _.cloneDeep(provider)
411
+
412
+ this.$dialog.success(this.$t('shared.forms.saved'))
413
+ this.$emit('update:provider', providerEvent)
414
+ this.$emit('input', provider)
415
+ } catch (e) {
416
+ this.$dialog.error(
417
+ this.$t('windward.integrations.shared.error.save_failed')
418
+ )
419
+ }
420
+
421
+ // Create a new provider to clear the form if the original value is empty aka new
422
+ if (_.isEmpty(this.value)) {
423
+ this.provider = new LtiProvider({
424
+ role_metadata: {},
425
+ metadata: {},
426
+ version: '1.3',
427
+ })
428
+ }
429
+ },
430
+ async onSave() {
431
+ this.$refs.form.validate()
432
+ if (this.formValid) {
433
+ await this.save()
434
+ }
435
+ },
436
+
437
+ async onSaveAndNew() {
438
+ this.$refs.form.validate()
439
+ if (this.formValid) {
440
+ await this.save()
441
+ }
442
+ },
443
+ },
444
+ }
445
+ </script>