Skip to content

Commit 4d0b48f

Browse files
Merge pull request #1697 from serlo/move-spreadsheet-model-into-schema
move spreadsheet 'model' into 'schema'
2 parents 616546c + d47e59c commit 4d0b48f

File tree

6 files changed

+77
-131
lines changed

6 files changed

+77
-131
lines changed

__tests__/__utils__/services.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ import { v4 as uuidv4 } from 'uuid'
99

1010
import type { Identity, KratosDB } from '~/context/auth-services'
1111
import { Model } from '~/internals/graphql'
12-
import type { MajorDimension } from '~/model'
12+
13+
enum MajorDimension {
14+
Rows = 'ROWS',
15+
Columns = 'COLUMNS',
16+
}
1317

1418
export class MockKratos {
1519
identities: Identity[] = []

__tests__/schema/uuid/user.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@ import {
1616
createFakeIdentity,
1717
} from '../../__utils__'
1818
import { Model } from '~/internals/graphql'
19-
import { MajorDimension } from '~/model'
2019
import { Instance } from '~/types'
2120

21+
enum MajorDimension {
22+
Rows = 'ROWS',
23+
Columns = 'COLUMNS',
24+
}
25+
2226
const client = new Client()
2327
const adminUserId = 1
2428
const loginUserId = 9

packages/server/src/internals/data-source.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
import { RESTDataSource } from 'apollo-datasource-rest'
22

33
import { Context } from '~/context'
4-
import { createGoogleSpreadsheetApiModel, createChatModel } from '~/model'
4+
import { createChatModel } from '~/model'
55
import { createKratosModel } from '~/model/kratos'
66
import { createMailchimpModel } from '~/model/mailchimp'
77

88
export class ModelDataSource extends RESTDataSource {
9-
public googleSpreadsheetApi: ReturnType<
10-
typeof createGoogleSpreadsheetApiModel
11-
>
129
public chat: ReturnType<typeof createChatModel>
1310
public mailchimp: ReturnType<typeof createMailchimpModel>
1411
public kratos: ReturnType<typeof createKratosModel>
@@ -22,7 +19,6 @@ export class ModelDataSource extends RESTDataSource {
2219
super()
2320

2421
this.chat = createChatModel({ context })
25-
this.googleSpreadsheetApi = createGoogleSpreadsheetApiModel({ context })
2622
this.mailchimp = createMailchimpModel()
2723
this.kratos = createKratosModel({ context })
2824
}

packages/server/src/model/google-spreadsheet-api.ts

-111
This file was deleted.

packages/server/src/model/index.ts

-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
import { createChatModel } from './chat'
2-
import { createGoogleSpreadsheetApiModel } from './google-spreadsheet-api'
32
import { createKratosModel } from './kratos'
43
import { createMailchimpModel } from './mailchimp'
54

65
export * from './chat'
7-
export * from './google-spreadsheet-api'
86

97
export const modelFactories = {
108
chat: createChatModel,
11-
googleSpreadsheetApi: createGoogleSpreadsheetApiModel,
129
mailChimp: createMailchimpModel,
1310
kratos: createKratosModel,
1411
}

packages/server/src/schema/uuid/user/resolvers.ts

+66-10
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import * as serloAuth from '@serlo/authorization'
22
import { instanceToScope, Scope } from '@serlo/authorization'
33
import { createHash } from 'crypto'
44
import { array as A, either as E, function as F, option as O } from 'fp-ts'
5+
import { NonEmptyArray } from 'fp-ts/lib/NonEmptyArray'
56
import * as t from 'io-ts'
67
import * as R from 'ramda'
8+
import { URL } from 'url'
79

810
import { resolveUnrevisedEntityIds } from '../abstract-entity/resolvers'
911
import { UuidResolver } from '../abstract-uuid/resolvers'
@@ -25,7 +27,6 @@ import {
2527
generateRole,
2628
isGlobalRole,
2729
} from '~/internals/graphql'
28-
import { CellValues, MajorDimension } from '~/model'
2930
import { EntityDecoder, UserDecoder } from '~/model/decoder'
3031
import {
3132
getPermissionsForRole,
@@ -37,6 +38,11 @@ import { createThreadResolvers } from '~/schema/thread/utils'
3738
import { createUuidResolvers } from '~/schema/uuid/abstract-uuid/utils'
3839
import { Instance, Resolvers } from '~/types'
3940

41+
enum MajorDimension {
42+
Rows = 'ROWS',
43+
Columns = 'COLUMNS',
44+
}
45+
4046
export const ActiveUserIdsResolver = createCachedResolver<
4147
Record<string, never>,
4248
number[]
@@ -200,12 +206,12 @@ export const resolvers: Resolvers = {
200206
User: {
201207
...createUuidResolvers(),
202208
...createThreadResolvers(),
203-
async motivation(user, _args, context) {
209+
async motivation(user, _args, _context) {
210+
const spreadsheetId = process.env.GOOGLE_SPREADSHEET_API_MOTIVATION
211+
const range = 'Formularantworten!B:D'
212+
204213
return F.pipe(
205-
await context.dataSources.model.googleSpreadsheetApi.getValues({
206-
spreadsheetId: process.env.GOOGLE_SPREADSHEET_API_MOTIVATION,
207-
range: 'Formularantworten!B:D',
208-
}),
214+
await getSpreadsheetValues({ spreadsheetId, range }),
209215
E.mapLeft(addContext({ location: 'motivationSpreadsheet' })),
210216
E.getOrElse(consumeErrorEvent([] as string[][])),
211217
A.findLast(
@@ -622,11 +628,14 @@ async function fetchActivityByType(
622628
return result
623629
}
624630

625-
async function activeDonorIDs(context: Context) {
631+
async function activeDonorIDs(_context: Context) {
632+
const spreadsheetId = process.env.GOOGLE_SPREADSHEET_API_ACTIVE_DONORS
633+
const range = 'Tabellenblatt1!A:A'
634+
626635
return F.pipe(
627-
await context.dataSources.model.googleSpreadsheetApi.getValues({
628-
spreadsheetId: process.env.GOOGLE_SPREADSHEET_API_ACTIVE_DONORS,
629-
range: 'Tabellenblatt1!A:A',
636+
await getSpreadsheetValues({
637+
spreadsheetId,
638+
range,
630639
majorDimension: MajorDimension.Columns,
631640
}),
632641
extractIDsFromFirstColumn,
@@ -675,3 +684,50 @@ async function deleteKratosUser(
675684
await authServices.kratos.admin.deleteIdentity({ id: identity.id })
676685
}
677686
}
687+
688+
type CellValues = NonEmptyArray<string[]>
689+
690+
interface GetSpreadsheetValuesArgs {
691+
spreadsheetId: string
692+
range: string
693+
majorDimension?: MajorDimension
694+
}
695+
696+
async function getSpreadsheetValues(
697+
args: GetSpreadsheetValuesArgs,
698+
): Promise<E.Either<ErrorEvent, CellValues>> {
699+
const { spreadsheetId, range } = args
700+
const majorDimension = args.majorDimension ?? MajorDimension.Rows
701+
const url = new URL(
702+
`https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${range}`,
703+
)
704+
url.searchParams.append('majorDimension', majorDimension)
705+
const apiSecret = process.env.GOOGLE_SPREADSHEET_API_SECRET
706+
url.searchParams.append('key', apiSecret)
707+
708+
const specifyErrorLocation = E.mapLeft(
709+
addContext({
710+
location: 'googleSpreadSheetApi',
711+
locationContext: { ...args },
712+
}),
713+
)
714+
715+
try {
716+
const response = await fetch(url.toString())
717+
const data = (await response.json()) as { values?: string[][] }
718+
719+
if (
720+
!data.values ||
721+
!Array.isArray(data.values) ||
722+
data.values.length === 0
723+
) {
724+
return specifyErrorLocation(
725+
E.left({ error: new Error('invalid response or empty range') }),
726+
)
727+
}
728+
729+
return specifyErrorLocation(E.right(data.values as CellValues))
730+
} catch (error) {
731+
return specifyErrorLocation(E.left({ error: E.toError(error) }))
732+
}
733+
}

0 commit comments

Comments
 (0)