Skip to content

Commit

Permalink
Merge branch 'main' into EW-1006
Browse files Browse the repository at this point in the history
  • Loading branch information
mkreuzkam-cap authored Oct 8, 2024
2 parents d74dc3a + d95880a commit fe7dd8c
Show file tree
Hide file tree
Showing 107 changed files with 6,840 additions and 654 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,11 @@ data:

# ========== Start of the CTL seed data configuration section.
echo "Inserting ctl seed data secrets to external-tools..."

# Encrypt secrets of external tools that contain an lti11 config.
$CTL_SEED_SECRET_ONLINE_DIA_MATHE=$(node scripts/secret.js -s $AES_KEY -e $CTL_SEED_SECRET_ONLINE_DIA_MATHE)
$CTL_SEED_SECRET_ONLINE_DIA_DEUTSCH=$(node scripts/secret.js -s $AES_KEY -e $CTL_SEED_SECRET_ONLINE_DIA_DEUTSCH)

mongosh $DATABASE__URL --quiet --eval 'db.getCollection("external-tools").updateOne(
{
"name": "Product Test Onlinediagnose Grundschule - Mathematik",
Expand Down
1 change: 1 addition & 0 deletions ansible/roles/schulcloud-server-tspsync/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SERVER_TSP_SYNC_CRONJOB_SCHEDULE: "9 3 * * *"
30 changes: 30 additions & 0 deletions ansible/roles/schulcloud-server-tspsync/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,33 @@
when: not WITH_TSP
tags:
- cronjob

- name: API TSP Sync CronJob
kubernetes.core.k8s:
kubeconfig: ~/.kube/config
namespace: "{{ NAMESPACE }}"
template: api-tsp-sync-cronjob.yml.j2
when: WITH_TSP_SYNC
tags:
- cronjob

- name: remove API TSP Sync CronJob
kubernetes.core.k8s:
kubeconfig: ~/.kube/config
state: absent
api_version: batch/v1
kind: CronJob
namespace: "{{ NAMESPACE }}"
name: api-tsp-sync-cronjob
when: not WITH_TSP_SYNC
tags:
- cronjob

- name: API TSP Sync CronJob ConfigMap
kubernetes.core.k8s:
kubeconfig: ~/.kube/config
namespace: "{{ NAMESPACE }}"
template: api-tsp-sync-cronjob-configmap.yml.j2
state: "{{ 'present' if WITH_TSP_SYNC|bool else 'absent'}}"
tags:
- configmap
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
apiVersion: v1
kind: ConfigMap
metadata:
namespace: {{ NAMESPACE }}
name: api-tsp-sync-cronjob-configmap
labels:
app: api-tsp-sync-cronjob
data:
NODE_OPTIONS: "--max-old-space-size=3072"
NEST_LOG_LEVEL: "error"
EXIT_ON_ERROR: "true"
FEATURE_IDENTITY_MANAGEMENT_ENABLED: "{{ FEATURE_IDENTITY_MANAGEMENT_ENABLED }}"
FEATURE_IDENTITY_MANAGEMENT_STORE_ENABLED: "{{ FEATURE_IDENTITY_MANAGEMENT_STORE_ENABLED }}"
FEATURE_IDENTITY_MANAGEMENT_LOGIN_ENABLED: "{{ FEATURE_IDENTITY_MANAGEMENT_LOGIN_ENABLED }}"
IDENTITY_MANAGEMENT__INTERNAL_URI: "{{ IDENTITY_MANAGEMENT__INTERNAL_URI }}"
IDENTITY_MANAGEMENT__EXTERNAL_URI: "{{ IDENTITY_MANAGEMENT__EXTERNAL_URI }}"
IDENTITY_MANAGEMENT__TENANT: "{{ IDENTITY_MANAGEMENT__TENANT }}"
IDENTITY_MANAGEMENT__CLIENTID: "{{ IDENTITY_MANAGEMENT__CLIENTID }}"
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
apiVersion: batch/v1
kind: CronJob
metadata:
namespace: {{ NAMESPACE }}
labels:
app: api-tsp-sync-cronjob
app.kubernetes.io/part-of: schulcloud-verbund
app.kubernetes.io/version: {{ SCHULCLOUD_SERVER_IMAGE_TAG }}
app.kubernetes.io/name: api-tsp-sync-cronjob
app.kubernetes.io/component: sync
app.kubernetes.io/managed-by: ansible
git.branch: {{ SCHULCLOUD_SERVER_BRANCH_NAME }}
git.repo: {{ SCHULCLOUD_SERVER_REPO_NAME }}
name: api-tsp-sync-cronjob
spec:
concurrencyPolicy: Forbid
schedule: "{{ SERVER_TSP_SYNC_CRONJOB_SCHEDULE }}"
jobTemplate:
metadata:
labels:
app: api-tsp-sync-cronjob
app.kubernetes.io/part-of: schulcloud-verbund
app.kubernetes.io/version: {{ SCHULCLOUD_SERVER_IMAGE_TAG }}
app.kubernetes.io/name: api-tsp-sync-cronjob
app.kubernetes.io/component: sync
app.kubernetes.io/managed-by: ansible
git.branch: {{ SCHULCLOUD_SERVER_BRANCH_NAME }}
git.repo: {{ SCHULCLOUD_SERVER_REPO_NAME }}
spec:
template:
metadata:
labels:
app: api-tsp-sync-cronjob
app.kubernetes.io/part-of: schulcloud-verbund
app.kubernetes.io/version: {{ SCHULCLOUD_SERVER_IMAGE_TAG }}
app.kubernetes.io/name: api-tsp-sync-cronjob
app.kubernetes.io/component: sync
app.kubernetes.io/managed-by: ansible
git.branch: {{ SCHULCLOUD_SERVER_BRANCH_NAME }}
git.repo: {{ SCHULCLOUD_SERVER_REPO_NAME }}
spec:
securityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
runAsNonRoot: true
containers:
- name: api-tsp-sync-cronjob
image: {{ SCHULCLOUD_SERVER_IMAGE }}:{{ SCHULCLOUD_SERVER_IMAGE_TAG }}
envFrom:
- configMapRef:
name: api-tsp-sync-cronjob-configmap
- secretRef:
name: api-secret
command: ['/bin/sh', '-c']
args: ['npm run nest:start:sync tsp']
resources:
limits:
cpu: {{ API_CPU_LIMITS|default("2000m", true) }}
memory: {{ API_MEMORY_LIMITS|default("2Gi", true) }}
requests:
cpu: {{ API_CPU_REQUESTS|default("100m", true) }}
memory: {{ API_MEMORY_REQUESTS|default("150Mi", true) }}
restartPolicy: OnFailure
{% if AFFINITY_ENABLE is defined and AFFINITY_ENABLE|bool %}
affinity:
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 20
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app.kubernetes.io/part-of
operator: In
values:
- schulcloud-verbund
topologyKey: "kubernetes.io/hostname"
namespaceSelector: {}
- weight: 10
podAffinityTerm:
labelSelector:
matchExpressions:
- key: git.repo
operator: In
values:
- {{ SCHULCLOUD_SERVER_REPO_NAME }}
topologyKey: "kubernetes.io/hostname"
namespaceSelector: {}
- weight: 10
podAffinityTerm:
labelSelector:
matchExpressions:
- key: git.branch
operator: In
values:
- {{ SCHULCLOUD_SERVER_BRANCH_NAME }}
topologyKey: "kubernetes.io/hostname"
namespaceSelector: {}
- weight: 10
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app.kubernetes.io/version
operator: In
values:
- {{ SCHULCLOUD_SERVER_IMAGE_TAG }}
topologyKey: "kubernetes.io/hostname"
namespaceSelector: {}
{% endif %}
3 changes: 3 additions & 0 deletions apps/server/src/apps/server.app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { AccountService } from '@modules/account';
import { SystemRule } from '@modules/authorization/domain/rules';

Check warning on line 7 in apps/server/src/apps/server.app.ts

View workflow job for this annotation

GitHub Actions / nest_lint

'@modules/authorization/domain/rules' import is restricted from being used by a pattern. Do not deep import from a module

Check warning on line 7 in apps/server/src/apps/server.app.ts

View workflow job for this annotation

GitHub Actions / nest_lint

'@modules/authorization/domain/rules' import is restricted from being used by a pattern. Do not deep import from a module

import { ColumnBoardService } from '@modules/board';
import { ContextExternalToolService } from '@src/modules/tool/context-external-tool';
import { CollaborativeStorageUc } from '@modules/collaborative-storage/uc/collaborative-storage.uc';

Check warning on line 11 in apps/server/src/apps/server.app.ts

View workflow job for this annotation

GitHub Actions / nest_lint

'@modules/collaborative-storage/uc/collaborative-storage.uc' import is restricted from being used by a pattern. Do not deep import from a module

Check warning on line 11 in apps/server/src/apps/server.app.ts

View workflow job for this annotation

GitHub Actions / nest_lint

'@modules/collaborative-storage/uc/collaborative-storage.uc' import is restricted from being used by a pattern. Do not deep import from a module
import { GroupService } from '@modules/group';
import { InternalServerModule } from '@modules/internal-server';
Expand Down Expand Up @@ -94,6 +95,8 @@ async function bootstrap() {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
feathersExpress.services['nest-column-board-service'] = nestApp.get(ColumnBoardService);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
feathersExpress.services['nest-context-external-tool-service'] = nestApp.get(ContextExternalToolService);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
feathersExpress.services['nest-system-rule'] = nestApp.get(SystemRule);
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment
feathersExpress.services['nest-orm'] = orm;
Expand Down
2 changes: 0 additions & 2 deletions apps/server/src/infra/schulconnex-client/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
export { SchulconnexRestClientOptions } from './schulconnex-rest-client-options';
export { SchulconnexClientModule } from './schulconnex-client.module';
export { SchulconnexRestClient } from './schulconnex-rest-client';
export * from './response';
export { schulconnexResponseFactory, schulconnexPoliciesInfoResponseFactory } from './testing';
export { SchulconnexClientConfig } from './schulconnex-client-config';
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@ export { SchulconnexPoliciesInfoTargetResponse } from './schulconnex-policies-in
export { SchulconnexPoliciesInfoResponse } from './schulconnex-policies-info-response';
export { SchulconnexPoliciesInfoActionType } from './schulconnex-policies-info-action-type';
export { SchulconnexPoliciesInfoPermissionResponse } from './schulconnex-policies-info-permission-response';
export { SchulconnexPoliciesInfoAccessControlResponse } from './schulconnex-policies-info-access-control-response';
export { SchulconnexPoliciesInfoErrorDescriptionResponse } from './schulconnex-policies-info-error-description-response';
export { SchulconnexPoliciesInfoLicenseResponse } from './schulconnex-policies-info-license-response';
export { SchulconnexPoliciesInfoErrorResponse } from './schulconnex-policies-info-error-response';
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Type } from 'class-transformer';
import { IsObject, IsString, ValidateNested } from 'class-validator';
import { SchulconnexPoliciesInfoErrorDescriptionResponse } from './schulconnex-policies-info-error-description-response';

export class SchulconnexPoliciesInfoAccessControlResponse {
@IsString()
'@type'!: string;

@IsObject()
@ValidateNested()
@Type(() => SchulconnexPoliciesInfoErrorDescriptionResponse)
error!: SchulconnexPoliciesInfoErrorDescriptionResponse;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { IsString } from 'class-validator';

export class SchulconnexPoliciesInfoErrorDescriptionResponse {
@IsString()
code!: string;

@IsString()
value!: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Type } from 'class-transformer';
import { IsObject, ValidateNested } from 'class-validator';
import { SchulconnexPoliciesInfoAccessControlResponse } from './schulconnex-policies-info-access-control-response';

export class SchulconnexPoliciesInfoErrorResponse {
@IsObject()
@ValidateNested()
@Type(() => SchulconnexPoliciesInfoAccessControlResponse)
access_control!: SchulconnexPoliciesInfoAccessControlResponse;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Type } from 'class-transformer';
import { IsArray, IsObject, ValidateNested } from 'class-validator';
import { SchulconnexPoliciesInfoPermissionResponse } from './schulconnex-policies-info-permission-response';
import { SchulconnexPoliciesInfoTargetResponse } from './schulconnex-policies-info-target-response';

export class SchulconnexPoliciesInfoLicenseResponse {
@IsObject()
@ValidateNested()
@Type(() => SchulconnexPoliciesInfoTargetResponse)
target!: SchulconnexPoliciesInfoTargetResponse;

@IsArray()
@ValidateNested({ each: true })
@Type(() => SchulconnexPoliciesInfoPermissionResponse)
permission!: SchulconnexPoliciesInfoPermissionResponse[];
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { IsArray } from 'class-validator';
import { IsArray, IsString } from 'class-validator';
import { SchulconnexPoliciesInfoActionType } from './schulconnex-policies-info-action-type';

export class SchulconnexPoliciesInfoPermissionResponse {
@IsArray()
@IsString({ each: true })
action!: SchulconnexPoliciesInfoActionType[];
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import { Type } from 'class-transformer';
import { IsArray, IsObject, ValidateNested } from 'class-validator';
import { SchulconnexPoliciesInfoPermissionResponse } from './schulconnex-policies-info-permission-response';
import { SchulconnexPoliciesInfoTargetResponse } from './schulconnex-policies-info-target-response';
import { PolymorphicArrayTransform } from '@shared/controller';
import { ClassConstructor } from 'class-transformer';
import { IsArray, ValidateNested } from 'class-validator';
import { SchulconnexPoliciesInfoErrorResponse } from './schulconnex-policies-info-error-response';
import { SchulconnexPoliciesInfoLicenseResponse } from './schulconnex-policies-info-license-response';

export class SchulconnexPoliciesInfoResponse {
@IsObject()
@ValidateNested()
@Type(() => SchulconnexPoliciesInfoTargetResponse)
target!: SchulconnexPoliciesInfoTargetResponse;
const policiesInfoDiscriminator = (
obj: unknown
): ClassConstructor<SchulconnexPoliciesInfoLicenseResponse | SchulconnexPoliciesInfoErrorResponse> =>
typeof obj === 'object' && obj !== null && 'target' in obj && 'permission' in obj
? SchulconnexPoliciesInfoLicenseResponse
: SchulconnexPoliciesInfoErrorResponse;

export class SchulconnexPoliciesInfoResponse {
@IsArray()
@ValidateNested({ each: true })
@Type(() => SchulconnexPoliciesInfoPermissionResponse)
permission!: SchulconnexPoliciesInfoPermissionResponse[];
@PolymorphicArrayTransform(policiesInfoDiscriminator)
data!: (SchulconnexPoliciesInfoLicenseResponse | SchulconnexPoliciesInfoErrorResponse)[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Type } from 'class-transformer';
import { IsObject, IsOptional, ValidateNested } from 'class-validator';
import { SchulconnexGeburtResponse } from './schulconnex-geburt-response';
import { SchulconnexNameResponse } from './schulconnex-name-response';
import 'reflect-metadata';

export class SchulconnexPersonResponse {
@IsObject()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ export interface SchulconnexApiInterface {

getPersonenInfo(params: SchulconnexPersonenInfoParams): Promise<SchulconnexResponse[]>;

getPoliciesInfo(accessToken: string, options?: { overrideUrl: string }): Promise<SchulconnexPoliciesInfoResponse[]>;
getPoliciesInfo(accessToken: string, options?: { overrideUrl: string }): Promise<SchulconnexPoliciesInfoResponse>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ import { axiosResponseFactory } from '@shared/testing';
import { Logger } from '@src/core/logger';
import { of } from 'rxjs';
import { SchulconnexConfigurationMissingLoggable } from './loggable';
import { SchulconnexPoliciesInfoResponse, SchulconnexResponse } from './response';
import {
SchulconnexPoliciesInfoLicenseResponse,
SchulconnexPoliciesInfoResponse,
SchulconnexResponse,
} from './response';
import { SchulconnexRestClient } from './schulconnex-rest-client';
import { SchulconnexRestClientOptions } from './schulconnex-rest-client-options';
import { schulconnexPoliciesInfoResponseFactory, schulconnexResponseFactory } from './testing';
import { schulconnexPoliciesInfoLicenseResponseFactory, schulconnexResponseFactory } from './testing';

describe(SchulconnexRestClient.name, () => {
let client: SchulconnexRestClient;
Expand Down Expand Up @@ -199,7 +203,8 @@ describe(SchulconnexRestClient.name, () => {
describe('when requesting policies-info', () => {
const setup = () => {
const accessToken = 'accessToken';
const response: SchulconnexPoliciesInfoResponse[] = schulconnexPoliciesInfoResponseFactory.buildList(1);
const response: SchulconnexPoliciesInfoLicenseResponse[] =
schulconnexPoliciesInfoLicenseResponseFactory.buildList(1);

httpService.get.mockReturnValueOnce(of(axiosResponseFactory.build({ data: response })));

Expand All @@ -224,7 +229,7 @@ describe(SchulconnexRestClient.name, () => {
it('should return the response', async () => {
const { accessToken } = setup();

const result: SchulconnexPoliciesInfoResponse[] = await client.getPoliciesInfo(accessToken);
const result: SchulconnexPoliciesInfoResponse = await client.getPoliciesInfo(accessToken);

expect(result).toBeDefined();
});
Expand All @@ -234,7 +239,8 @@ describe(SchulconnexRestClient.name, () => {
const setup = () => {
const accessToken = 'accessToken';
const customUrl = 'https://override.url/policies-info';
const response: SchulconnexPoliciesInfoResponse[] = schulconnexPoliciesInfoResponseFactory.buildList(1);
const response: SchulconnexPoliciesInfoLicenseResponse[] =
schulconnexPoliciesInfoLicenseResponseFactory.buildList(1);

httpService.get.mockReturnValueOnce(of(axiosResponseFactory.build({ data: response })));

Expand Down
Loading

0 comments on commit fe7dd8c

Please sign in to comment.