com.jetbrains.edu.learning.stepik.builtInServer.StepikRestService.java Source code

Java tutorial

Introduction

Here is the source code for com.jetbrains.edu.learning.stepik.builtInServer.StepikRestService.java

Source

/*
 * Copyright 2000-2017 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jetbrains.edu.learning.stepik.builtInServer;

import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.wm.IdeFrame;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.ui.AppIcon;
import com.jetbrains.edu.learning.EduSettings;
import com.jetbrains.edu.learning.authUtils.OAuthRestService;
import com.jetbrains.edu.learning.courseFormat.Lesson;
import com.jetbrains.edu.learning.courseFormat.Section;
import com.jetbrains.edu.learning.stepik.*;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.QueryStringDecoder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.ide.RestService;

import javax.swing.*;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.jetbrains.edu.learning.stepik.builtInServer.EduBuiltInServerUtils.*;

public class StepikRestService extends OAuthRestService {
    private static final Logger LOG = Logger.getInstance(StepikRestService.class.getName());
    private static final Pattern OPEN_COURSE_PATTERN = Pattern
            .compile("/" + StepikNames.EDU_STEPIK_SERVICE_NAME + "\\?link=.+");
    private static final Pattern COURSE_PATTERN = Pattern
            .compile("https://stepik\\.org/lesson(?:/[a-zA-Z\\-]*-|/)(\\d+)/step/(\\d+)");
    private static final Pattern OAUTH_CODE_PATTERN = Pattern.compile(
            "/" + RestService.PREFIX + "/" + StepikNames.EDU_STEPIK_SERVICE_NAME + "/oauth" + "\\?code=(\\w+)");

    public StepikRestService() {
        super(StepikNames.STEPIK);
    }

    @NotNull
    private static String log(@NotNull String message) {
        LOG.info(message);
        return message;
    }

    @NotNull
    @Override
    protected String getServiceName() {
        return StepikNames.EDU_STEPIK_SERVICE_NAME;
    }

    @Override
    protected boolean isMethodSupported(@NotNull HttpMethod method) {
        return method == HttpMethod.GET;
    }

    @Override
    protected boolean isPrefixlessAllowed() {
        return true;
    }

    @Override
    protected boolean isHostTrusted(@NotNull FullHttpRequest request)
            throws InterruptedException, InvocationTargetException {
        String uri = request.uri();
        Matcher codeMatcher = OAUTH_CODE_PATTERN.matcher(uri);
        if (request.method() == HttpMethod.GET && codeMatcher.matches()) {
            return true;
        }
        return super.isHostTrusted(request);
    }

    @Nullable
    @Override
    public String execute(@NotNull QueryStringDecoder urlDecoder, @NotNull FullHttpRequest request,
            @NotNull ChannelHandlerContext context) throws IOException {
        String uri = urlDecoder.uri();
        LOG.info("Request: " + uri);

        Matcher matcher = OPEN_COURSE_PATTERN.matcher(uri);
        if (matcher.matches()) {
            int courseId;
            int stepId;
            String link = getStringParameter(StepikNames.LINK, urlDecoder);

            if (link == null) {
                return log("The link parameter was not found");
            }

            LOG.info("Try to open a course: " + link);

            QueryStringDecoder linkDecoder = new QueryStringDecoder(link);

            matcher = COURSE_PATTERN.matcher(linkDecoder.path());

            if (!matcher.matches()) {
                return log("Unrecognized the link parameter");
            }

            int lessonId;
            int stepIndex;
            try {
                lessonId = Integer.parseInt(matcher.group(1));
                stepIndex = Integer.parseInt(matcher.group(2));
            } catch (NumberFormatException e) {
                return log("Unrecognized the link");
            }

            int unitId = getIntParameter("unit", linkDecoder);

            if (unitId == -1) {
                return log("Unrecognized the Unit id");
            }

            StepikWrappers.Unit unit = StepikConnector.getUnit(unitId);
            if (unit.getId() == 0) {
                return log("Unrecognized the Unit id");
            }

            Section section = StepikConnector.getSection(unit.getSection());
            courseId = section.getCourseId();
            if (courseId == 0) {
                return log("Unrecognized the course id");
            }
            Lesson lesson = StepikConnector.getLesson(lessonId);
            List<Integer> stepIds = lesson.steps;

            if (stepIds.isEmpty()) {
                return log("Unrecognized the step id");
            }
            stepId = stepIds.get(stepIndex - 1);

            LOG.info(String.format("Try to open a course: courseId=%s, stepId=%s", courseId, stepId));

            if (focusOpenProject(courseId, stepId) || openRecentProject(courseId, stepId)
                    || createProject(courseId, stepId)) {
                RestService.sendOk(request, context);
                LOG.info("Course opened: " + courseId);
                return null;
            }

            RestService.sendStatus(HttpResponseStatus.NOT_FOUND, false, context.channel());
            String message = "A project didn't found or created";
            LOG.info(message);
            return message;
        }

        Matcher codeMatcher = OAUTH_CODE_PATTERN.matcher(uri);
        if (codeMatcher.matches()) {
            String code = getStringParameter("code", urlDecoder);
            if (code != null) {
                StepicUser user = StepikAuthorizedClient.login(code, StepikConnector.getOAuthRedirectUrl());
                if (user != null) {
                    EduSettings.getInstance().setUser(user);
                    showOkPage(request, context);
                    showStepikNotification(NotificationType.INFORMATION,
                            "Logged in as " + user.getFirstName() + " " + user.getLastName());
                    focusOnApplicationWindow();
                    return null;
                }
            }

            showStepikNotification(NotificationType.ERROR, "Failed to log in");
            return sendErrorResponse(request, context, "Couldn't find code parameter for Stepik OAuth");
        }

        RestService.sendStatus(HttpResponseStatus.BAD_REQUEST, false, context.channel());
        String message = "Unknown command: " + uri;
        LOG.info(message);
        return message;
    }

    private static void focusOnApplicationWindow() {
        JFrame frame = WindowManager.getInstance().findVisibleFrame();
        ApplicationManager.getApplication().invokeLater(() -> {
            AppIcon.getInstance().requestFocus((IdeFrame) frame);
            frame.toFront();
        });
    }

    private static void showStepikNotification(@NotNull NotificationType notificationType, @NotNull String text) {
        Notification notification = new Notification("Stepik", "Stepik", text, notificationType);
        notification.notify(null);
    }
}