Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

move spreadsheet 'model' into 'schema' #1697

Merged
merged 1 commit into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
}
76 changes: 66 additions & 10 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 All @@ -25,7 +27,6 @@ import {
generateRole,
isGlobalRole,
} from '~/internals/graphql'
import { CellValues, MajorDimension } from '~/model'
import { EntityDecoder, UserDecoder } from '~/model/decoder'
import {
getPermissionsForRole,
Expand All @@ -37,6 +38,11 @@ import { createThreadResolvers } from '~/schema/thread/utils'
import { createUuidResolvers } from '~/schema/uuid/abstract-uuid/utils'
import { Instance, Resolvers } from '~/types'

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

export const ActiveUserIdsResolver = createCachedResolver<
Record<string, never>,
number[]
Expand Down Expand Up @@ -200,12 +206,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 +628,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 +684,50 @@ async function deleteKratosUser(
await authServices.kratos.admin.deleteIdentity({ id: identity.id })
}
}

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) }))
}
}