Skip to content

Commit

Permalink
move spreadsheet 'model' into 'schema'
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreasHuber committed Jul 29, 2024
1 parent 616546c commit 2347853
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 130 deletions.
6 changes: 5 additions & 1 deletion __tests__/__utils__/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import { v4 as uuidv4 } from 'uuid'

import type { Identity, KratosDB } from '~/context/auth-services'
import { Model } from '~/internals/graphql'
import type { MajorDimension } from '~/model'

enum MajorDimension {
Rows = 'ROWS',
Columns = 'COLUMNS',
}

export class MockKratos {
identities: Identity[] = []
Expand Down
6 changes: 5 additions & 1 deletion __tests__/schema/uuid/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@ import {
createFakeIdentity,
} from '../../__utils__'
import { Model } from '~/internals/graphql'
import { MajorDimension } from '~/model'
import { Instance } from '~/types'

enum MajorDimension {
Rows = 'ROWS',
Columns = 'COLUMNS',
}

const client = new Client()
const adminUserId = 1
const loginUserId = 9
Expand Down
6 changes: 1 addition & 5 deletions packages/server/src/internals/data-source.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { RESTDataSource } from 'apollo-datasource-rest'

import { Context } from '~/context'
import { createGoogleSpreadsheetApiModel, createChatModel } from '~/model'
import { createChatModel } from '~/model'
import { createKratosModel } from '~/model/kratos'
import { createMailchimpModel } from '~/model/mailchimp'

export class ModelDataSource extends RESTDataSource {
public googleSpreadsheetApi: ReturnType<
typeof createGoogleSpreadsheetApiModel
>
public chat: ReturnType<typeof createChatModel>
public mailchimp: ReturnType<typeof createMailchimpModel>
public kratos: ReturnType<typeof createKratosModel>
Expand All @@ -22,7 +19,6 @@ export class ModelDataSource extends RESTDataSource {
super()

this.chat = createChatModel({ context })
this.googleSpreadsheetApi = createGoogleSpreadsheetApiModel({ context })
this.mailchimp = createMailchimpModel()
this.kratos = createKratosModel({ context })
}
Expand Down
111 changes: 0 additions & 111 deletions packages/server/src/model/google-spreadsheet-api.ts

This file was deleted.

3 changes: 0 additions & 3 deletions packages/server/src/model/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { createChatModel } from './chat'
import { createGoogleSpreadsheetApiModel } from './google-spreadsheet-api'
import { createKratosModel } from './kratos'
import { createMailchimpModel } from './mailchimp'

export * from './chat'
export * from './google-spreadsheet-api'

export const modelFactories = {
chat: createChatModel,
googleSpreadsheetApi: createGoogleSpreadsheetApiModel,
mailChimp: createMailchimpModel,
kratos: createKratosModel,
}
75 changes: 66 additions & 9 deletions packages/server/src/schema/uuid/user/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import * as serloAuth from '@serlo/authorization'
import { instanceToScope, Scope } from '@serlo/authorization'
import { createHash } from 'crypto'
import { array as A, either as E, function as F, option as O } from 'fp-ts'
import { NonEmptyArray } from 'fp-ts/lib/NonEmptyArray'
import * as t from 'io-ts'
import * as R from 'ramda'
import { URL } from 'url'

import { resolveUnrevisedEntityIds } from '../abstract-entity/resolvers'
import { UuidResolver } from '../abstract-uuid/resolvers'
Expand Down Expand Up @@ -200,12 +202,12 @@ export const resolvers: Resolvers = {
User: {
...createUuidResolvers(),
...createThreadResolvers(),
async motivation(user, _args, context) {
async motivation(user, _args, _context) {
const spreadsheetId = process.env.GOOGLE_SPREADSHEET_API_MOTIVATION
const range = 'Formularantworten!B:D'

return F.pipe(
await context.dataSources.model.googleSpreadsheetApi.getValues({
spreadsheetId: process.env.GOOGLE_SPREADSHEET_API_MOTIVATION,
range: 'Formularantworten!B:D',
}),
await getSpreadsheetValues({ spreadsheetId, range }),
E.mapLeft(addContext({ location: 'motivationSpreadsheet' })),
E.getOrElse(consumeErrorEvent([] as string[][])),
A.findLast(
Expand Down Expand Up @@ -622,11 +624,14 @@ async function fetchActivityByType(
return result
}

async function activeDonorIDs(context: Context) {
async function activeDonorIDs(_context: Context) {
const spreadsheetId = process.env.GOOGLE_SPREADSHEET_API_ACTIVE_DONORS
const range = 'Tabellenblatt1!A:A'

return F.pipe(
await context.dataSources.model.googleSpreadsheetApi.getValues({
spreadsheetId: process.env.GOOGLE_SPREADSHEET_API_ACTIVE_DONORS,
range: 'Tabellenblatt1!A:A',
await getSpreadsheetValues({
spreadsheetId,
range,
majorDimension: MajorDimension.Columns,
}),
extractIDsFromFirstColumn,
Expand Down Expand Up @@ -675,3 +680,55 @@ async function deleteKratosUser(
await authServices.kratos.admin.deleteIdentity({ id: identity.id })
}
}

enum MajorDimension {
Rows = 'ROWS',
Columns = 'COLUMNS',
}

type CellValues = NonEmptyArray<string[]>

interface GetSpreadsheetValuesArgs {
spreadsheetId: string
range: string
majorDimension?: MajorDimension
}

async function getSpreadsheetValues(
args: GetSpreadsheetValuesArgs,
): Promise<E.Either<ErrorEvent, CellValues>> {
const { spreadsheetId, range } = args
const majorDimension = args.majorDimension ?? MajorDimension.Rows
const url = new URL(
`https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${range}`,
)
url.searchParams.append('majorDimension', majorDimension)
const apiSecret = process.env.GOOGLE_SPREADSHEET_API_SECRET
url.searchParams.append('key', apiSecret)

const specifyErrorLocation = E.mapLeft(
addContext({
location: 'googleSpreadSheetApi',
locationContext: { ...args },
}),
)

try {
const response = await fetch(url.toString())
const data = (await response.json()) as { values?: string[][] }

if (
!data.values ||
!Array.isArray(data.values) ||
data.values.length === 0
) {
return specifyErrorLocation(
E.left({ error: new Error('invalid response or empty range') }),
)
}

return specifyErrorLocation(E.right(data.values as CellValues))
} catch (error) {
return specifyErrorLocation(E.left({ error: E.toError(error) }))
}
}

0 comments on commit 2347853

Please sign in to comment.